Add support for Foreign data wrappers, servers and user mappings.
parent
05e527505d
commit
3969e91563
|
@ -0,0 +1,759 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Implements Foreign Data Wrapper Node"""
|
||||
|
||||
import json
|
||||
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 as servers
|
||||
from pgadmin.utils.ajax import precondition_required
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
parse_priv_to_db
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class ForeignDataWrapperModule(CollectionNodeModule):
|
||||
"""
|
||||
class ForeignDataWrapperModule(CollectionNodeModule)
|
||||
|
||||
A module class for foreign data wrapper node derived from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the Foreign data wrapper module 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 foreign data wrapper, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
|
||||
NODE_TYPE = 'foreign_data_wrapper'
|
||||
COLLECTION_LABEL = gettext("Foreign Data Wrappers")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Method is used to initialize the Foreign data wrapper module and it's base module.
|
||||
|
||||
Args:
|
||||
*args:
|
||||
**kwargs:
|
||||
"""
|
||||
|
||||
self.min_ver = None
|
||||
self.max_ver = None
|
||||
|
||||
super(ForeignDataWrapperModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_nodes(self, gid, sid, did):
|
||||
"""
|
||||
Method is used to generate the browser collection node
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database Id
|
||||
"""
|
||||
yield self.generate_browser_collection_node(did)
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for foreign data wrapper, when any of the database node is initialized.
|
||||
|
||||
Returns: node type of the server module.
|
||||
"""
|
||||
return servers.ServerModule.NODE_TYPE
|
||||
|
||||
blueprint = ForeignDataWrapperModule(__name__)
|
||||
|
||||
|
||||
class ForeignDataWrapperView(PGChildNodeView):
|
||||
"""
|
||||
class ForeignDataWrapperView(PGChildNodeView)
|
||||
|
||||
A view class for foreign data wrapper node derived from PGChildNodeView. This class is
|
||||
responsible for all the stuff related to view like updating foreign data wrapper
|
||||
node, showing properties, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the ForeignDataWrapperView 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(gid, sid, did)
|
||||
- This function is used to list all the foreign data wrapper nodes within that collection.
|
||||
|
||||
* nodes(gid, sid, did)
|
||||
- This function will used to create all the child node within that collection.
|
||||
Here it will create all the foreign data wrapper node.
|
||||
|
||||
* properties(gid, sid, did, fid)
|
||||
- This function will show the properties of the selected foreign data wrapper node
|
||||
|
||||
* tokenizeOptions(option_value)
|
||||
- This function will tokenize the string stored in database
|
||||
|
||||
* create(gid, sid, did)
|
||||
- This function will create the new foreign data wrapper node
|
||||
|
||||
* delete(gid, sid, did, fid)
|
||||
- This function will delete the selected foreign data wrapper node
|
||||
|
||||
* update(gid, sid, did, fid)
|
||||
- This function will update the data for the selected foreign data wrapper node
|
||||
|
||||
* msql(gid, sid, did, fid)
|
||||
- This function is used to return modified SQL for the selected foreign data wrapper node
|
||||
|
||||
* get_sql(data, fid)
|
||||
- This function will generate sql from model data
|
||||
|
||||
* get_validators(gid, sid, did)
|
||||
- This function returns the validators for the selected foreign data wrapper node
|
||||
|
||||
* get_handlers(gid, sid, did)
|
||||
- This function returns the handlers for the selected foreign data wrapper node
|
||||
|
||||
* sql(gid, sid, did, fid):
|
||||
- This function will generate sql to show it in sql pane for the selected foreign data wrapper node.
|
||||
|
||||
* dependents(gid, sid, did, fid):
|
||||
- This function get the dependents and return ajax response for the foreign data wrapper node.
|
||||
|
||||
* dependencies(self, gid, sid, did, fid):
|
||||
- This function get the dependencies and return ajax response for the foreign data wrapper node.
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'fid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'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'}],
|
||||
'get_handlers': [{}, {'get': 'get_handlers'}],
|
||||
'get_validators': [{}, {'get': 'get_validators'}]
|
||||
})
|
||||
|
||||
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(
|
||||
"foreign_data_wrappers/js/foreign_data_wrappers.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
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90300:
|
||||
self.template_path = 'foreign_data_wrappers/sql/9.3_plus'
|
||||
else:
|
||||
self.template_path = 'foreign_data_wrappers/sql/9.1_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did):
|
||||
"""
|
||||
This function is used to list all the foreign data wrapper nodes within that collection.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for row in res['rows']:
|
||||
if row['fdwoptions'] is not None:
|
||||
row['fdwoptions'] = self.tokenize_options(row['fdwoptions'])
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did):
|
||||
"""
|
||||
This function will used to create all the child node within that collection.
|
||||
Here it will create all the foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
res = []
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['fdwoid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-foreign_data_wrapper"
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
def tokenize_options(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
e.g. database store the value as below
|
||||
key1=value1, key2=value2, key3=value3, ....
|
||||
This function will extract key and value from above string
|
||||
|
||||
Args:
|
||||
option_value: key value option/value pair read from database
|
||||
"""
|
||||
if option_value is not None:
|
||||
option_str = option_value.split(',')
|
||||
fdw_options = []
|
||||
for fdw_option in option_str:
|
||||
k, v = fdw_option.split('=', 1)
|
||||
fdw_options.append({'fdwoption': k, 'fdwvalue': v})
|
||||
return fdw_options
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will show the properties of the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), fid=fid)
|
||||
status, fdw_acl_res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=fdw_acl_res)
|
||||
|
||||
for row in fdw_acl_res['rows']:
|
||||
privilege = parse_priv_from_db(row)
|
||||
if row['deftype'] in res['rows'][0]:
|
||||
res['rows'][0][row['deftype']].append(privilege)
|
||||
else:
|
||||
res['rows'][0][row['deftype']] = [privilege]
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did):
|
||||
"""
|
||||
This function will create the foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Couldn't find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
if 'fdwacl' in data:
|
||||
data['fdwacl'] = parse_priv_to_db(data['fdwacl'], ['U'])
|
||||
|
||||
new_list = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data:
|
||||
for item in data['fdwoptions']:
|
||||
new_dict = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fdwoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fname=data['name'], conn=self.conn)
|
||||
|
||||
status, r_set = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['fdwoid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon='icon-foreign_data_wrapper'
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will update the data for the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid)
|
||||
try:
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Foreign Data Wrapper updated",
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will delete the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), fid=fid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
# drop foreign data wrapper node
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Data Wrapper dropped"),
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, fid=None):
|
||||
"""
|
||||
This function is used to return modified SQL for the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(data=sql,status=200)
|
||||
else:
|
||||
return make_json_response(data='-- Modified SQL --',status=200)
|
||||
|
||||
def get_sql(self, gid, sid, data, did, fid=None):
|
||||
"""
|
||||
This function will generate sql from model data.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
data: Contains the data of the selected foreign data wrapper node
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if fid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'added' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
data['fdwoptions']['added'] = new_list_add
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'changed' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
data['fdwoptions']['changed'] = new_list_change
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
else:
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
|
||||
new_list = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data:
|
||||
for item in data['fdwoptions']:
|
||||
new_dict = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fdwoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will generate sql to show it in sql pane for the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), fid=fid)
|
||||
status, fdw_acl_res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=fdw_acl_res)
|
||||
|
||||
for row in fdw_acl_res['rows']:
|
||||
privilege = parse_priv_from_db(row)
|
||||
if row['deftype'] in res['rows'][0]:
|
||||
res['rows'][0][row['deftype']].append(privilege)
|
||||
else:
|
||||
res['rows'][0][row['deftype']] = [privilege]
|
||||
|
||||
# To format privileges
|
||||
if 'fdwacl' in res['rows'][0]:
|
||||
res['rows'][0]['fdwacl'] = parse_priv_to_db(res['rows'][0]['fdwacl'], ['U'])
|
||||
|
||||
sql = ''
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], conn=self.conn)
|
||||
sql += "\n"
|
||||
|
||||
sql_header = """-- Foreign Data Wrapper: {0}
|
||||
|
||||
-- DROP FOREIGN DATA WRAPPER {0}
|
||||
|
||||
""".format(res['rows'][0]['name'])
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
return ajax_response(response=sql)
|
||||
|
||||
@check_precondition
|
||||
def get_validators(self, gid, sid, did):
|
||||
"""
|
||||
This function returns the validators for the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
res = [{'label': '', 'value': ''}]
|
||||
try:
|
||||
sql = render_template("/".join([self.template_path, 'validators.sql']), conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append({'label': row['fdwvalue'], 'value': row['fdwvalue']})
|
||||
|
||||
return make_json_response( data=res, status=200)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def get_handlers(self, gid, sid, did):
|
||||
"""
|
||||
This function returns the handlers for the selected foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
res = [{'label': '', 'value': ''}]
|
||||
try:
|
||||
sql = render_template("/".join([self.template_path, 'handlers.sql']), conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append({'label': row['fdwhan'], 'value': row['fdwhan']})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function get the dependents and return ajax response
|
||||
for the foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
dependents_result = self.get_dependents(self.conn, fid)
|
||||
return ajax_response(
|
||||
response=dependents_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def dependencies(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function get the dependencies and return ajax response
|
||||
for the foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign Data Wrapper ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(self.conn, fid)
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
ForeignDataWrapperView.register_node_view(blueprint)
|
|
@ -0,0 +1,754 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Implements Foreign Server Node"""
|
||||
|
||||
import json
|
||||
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 as servers
|
||||
from pgadmin.utils.ajax import precondition_required
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
parse_priv_to_db
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class ForeignServerModule(CollectionNodeModule):
|
||||
"""
|
||||
class ForeignServerModule(CollectionNodeModule)
|
||||
|
||||
A module class for foreign server node derived from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the Foreign server module and it's base module.
|
||||
|
||||
* get_nodes(gid, sid, did)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* script_load(self)
|
||||
- Load the module script for foreign server, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
|
||||
NODE_TYPE = 'foreign_server'
|
||||
COLLECTION_LABEL = gettext("Foreign Servers")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Method is used to initialize the Foreign server module and it's base module.
|
||||
|
||||
Args:
|
||||
*args:
|
||||
**kwargs:
|
||||
"""
|
||||
|
||||
self.min_ver = None
|
||||
self.max_ver = None
|
||||
|
||||
super(ForeignServerModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_nodes(self, gid, sid, did, fid):
|
||||
"""
|
||||
Method is used to generate the browser collection node
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
yield self.generate_browser_collection_node(fid)
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for foreign server, when any of the foreign data wrapper node is initialized.
|
||||
|
||||
Returns: node type of the server module.
|
||||
"""
|
||||
return servers.ServerModule.NODE_TYPE
|
||||
|
||||
blueprint = ForeignServerModule(__name__)
|
||||
|
||||
|
||||
class ForeignServerView(PGChildNodeView):
|
||||
"""
|
||||
class ForeignServerView(PGChildNodeView)
|
||||
|
||||
A view class for foreign server node derived from PGChildNodeView. This class is
|
||||
responsible for all the stuff related to view like updating foreign server
|
||||
node, showing properties, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the ForeignServerView 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(gid, sid, did, fid)
|
||||
- This function is used to list all the foreign server nodes within that collection.
|
||||
|
||||
* nodes(gid, sid, did, fid)
|
||||
- This function will used to create all the child node within that collection.
|
||||
Here it will create all the foreign server node.
|
||||
|
||||
* properties(gid, sid, did, fid, fsid)
|
||||
- This function will show the properties of the selected foreign server node
|
||||
|
||||
* tokenizeOptions(option_value)
|
||||
- This function will tokenize the string stored in database
|
||||
|
||||
* update(gid, sid, did, fid, fsid)
|
||||
- This function will update the data for the selected foreign server node
|
||||
|
||||
* create(gid, sid, did, fid)
|
||||
- This function will create the new foreign server node
|
||||
|
||||
* delete(gid, sid, did, fid, fsid)
|
||||
- This function will delete the selected foreign server node
|
||||
|
||||
* msql(gid, sid, did, fid, fsid)
|
||||
- This function is used to return modified SQL for the selected foreign server node
|
||||
|
||||
* get_sql(data, fid, fsid)
|
||||
- This function will generate sql from model data
|
||||
|
||||
* sql(gid, sid, did, fid, fsid):
|
||||
- This function will generate sql to show it in sql pane for the selected foreign server node.
|
||||
|
||||
* dependents(gid, sid, did, fid, fsid):
|
||||
- This function get the dependents and return ajax response for the foreign server node.
|
||||
|
||||
* dependencies(self, gid, sid, did, fid, fsid):
|
||||
- This function get the dependencies and return ajax response for the foreign server node.
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'fid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'fsid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'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'}]
|
||||
})
|
||||
|
||||
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(
|
||||
"foreign_servers/js/foreign_servers.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
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90300:
|
||||
self.template_path = 'foreign_servers/sql/9.3_plus'
|
||||
else:
|
||||
self.template_path = 'foreign_servers/sql/9.1_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function is used to list all the foreign server nodes within that collection.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
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, fid):
|
||||
"""
|
||||
This function will used to create all the child node within that collection.
|
||||
Here it will create all the foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
"""
|
||||
|
||||
res = []
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['fsrvid'],
|
||||
fid,
|
||||
row['name'],
|
||||
icon="icon-foreign_server"
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
def tokenizeOptions(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
e.g. database store the value as below
|
||||
key1=value1, key2=value2, key3=value3, ....
|
||||
This function will extract key and value from above string
|
||||
|
||||
Args:
|
||||
option_value: key value option/value pair read from database
|
||||
"""
|
||||
|
||||
if option_value is not None:
|
||||
option_str = option_value.split(',')
|
||||
fs_rv_options = []
|
||||
for fs_rv_option in option_str:
|
||||
k, v = fs_rv_option.split('=', 1)
|
||||
fs_rv_options.append({'fsrvoption': k, 'fsrvvalue': v})
|
||||
return fs_rv_options
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will show the properties of the selected foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), fsid=fsid)
|
||||
status, fs_rv_acl_res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=fs_rv_acl_res)
|
||||
|
||||
for row in fs_rv_acl_res['rows']:
|
||||
privilege = parse_priv_from_db(row)
|
||||
if row['deftype'] in res['rows'][0]:
|
||||
res['rows'][0][row['deftype']].append(privilege)
|
||||
else:
|
||||
res['rows'][0][row['deftype']] = [privilege]
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will create the foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Couldn't find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
try:
|
||||
if 'fsrvacl' in data:
|
||||
data['fsrvacl'] = parse_priv_to_db(data['fsrvacl'], ['U'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
new_list = []
|
||||
|
||||
if 'fsrvoptions' in data:
|
||||
for item in data['fsrvoptions']:
|
||||
new_dict = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fsrvoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_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, 'properties.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
status, r_set = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
r_set['rows'][0]['fsrvid'],
|
||||
fid,
|
||||
r_set['rows'][0]['name'],
|
||||
icon="icon-foreign_server"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will update the data for the selected foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
try:
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Foreign server updated",
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will delete the selected foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
# drop foreign server
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Server dropped"),
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, fid, fsid=None):
|
||||
"""
|
||||
This function is used to return modified SQL for the selected foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data='-- Modified SQL --',
|
||||
status=200
|
||||
)
|
||||
|
||||
def get_sql(self, gid, sid, data, did, fid, fsid=None):
|
||||
"""
|
||||
This function will generate sql from model data.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
data: Contains the data of the selected foreign server node
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if fsid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'added' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
data['fsrvoptions']['added'] = new_list_add
|
||||
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'changed' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
data['fsrvoptions']['changed'] = new_list_change
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
fdw_data = res['rows'][0]
|
||||
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
|
||||
new_list = []
|
||||
|
||||
if 'fsrvoptions' in data:
|
||||
for item in data['fsrvoptions']:
|
||||
new_dict = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fsrvoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will generate sql to show it in sql pane for the selected foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), fsid=fsid)
|
||||
status, fs_rv_acl_res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=fs_rv_acl_res)
|
||||
|
||||
for row in fs_rv_acl_res['rows']:
|
||||
privilege = parse_priv_from_db(row)
|
||||
if row['deftype'] in res['rows'][0]:
|
||||
res['rows'][0][row['deftype']].append(privilege)
|
||||
else:
|
||||
res['rows'][0][row['deftype']] = [privilege]
|
||||
|
||||
# To format privileges
|
||||
if 'fsrvacl' in res['rows'][0]:
|
||||
res['rows'][0]['fsrvacl'] = parse_priv_to_db(res['rows'][0]['fsrvacl'], ['U'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
sql = ''
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
|
||||
sql_header = """-- Foreign Server: {0}
|
||||
|
||||
-- DROP SERVER {0}
|
||||
|
||||
""".format(res['rows'][0]['name'])
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
return ajax_response(response=sql)
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function get the dependents and return ajax response
|
||||
for the foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
dependents_result = self.get_dependents(self.conn, fsid)
|
||||
|
||||
# Fetching dependents of foreign servers
|
||||
query = render_template("/".join([self.template_path, 'dependents.sql']), fsid=fsid)
|
||||
status, result = self.conn.execute_dict(query)
|
||||
if not status:
|
||||
internal_server_error(errormsg=result)
|
||||
|
||||
for row in result['rows']:
|
||||
dependents_result.append({'type': 'user_mapping', 'name': row['name'], 'field': 'normal' if (row['deptype'] == 'n') else ''})
|
||||
|
||||
return ajax_response(
|
||||
response=dependents_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def dependencies(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function get the dependencies and return ajax response
|
||||
for the foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign Data Wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(self.conn, fsid)
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
ForeignServerView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 476 B |
Binary file not shown.
After Width: | Height: | Size: 518 B |
|
@ -0,0 +1,180 @@
|
|||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection',
|
||||
'pgadmin.browser.server.privilege'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// Extend the browser's node model class to create a Options model
|
||||
var OptionsModel = pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
fsrvoption: undefined,
|
||||
fsrvvalue: undefined
|
||||
},
|
||||
|
||||
// Defining schema for the Options model
|
||||
schema: [
|
||||
{id: 'fsrvoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true},
|
||||
{id: 'fsrvvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true},
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
// Validation for the option name
|
||||
if (_.isUndefined(this.get('fsrvoption')) ||
|
||||
_.isNull(this.get('fsrvoption')) ||
|
||||
String(this.get('fsrvoption')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please enter an option name';
|
||||
this.errorModel.set('fsrvoption', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('fsrvoption');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Extend the browser's collection class for foreign server collection
|
||||
if (!pgBrowser.Nodes['coll-foreign_server']) {
|
||||
var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-foreign_server'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'foreign_server',
|
||||
label: '{{ _('Foreign Servers') }}',
|
||||
type: 'coll-foreign_server',
|
||||
columns: ['name','fsrvowner','description']
|
||||
});
|
||||
};
|
||||
|
||||
// Extend the browser's node class for foreign server node
|
||||
if (!pgBrowser.Nodes['foreign_server']) {
|
||||
pgAdmin.Browser.Nodes['foreign_server'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'foreign_data_wrapper',
|
||||
type: 'foreign_server',
|
||||
label: '{{ _('Foreign Server') }}',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
Init: function() {
|
||||
|
||||
// Avoid multiple registration of menus
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
/* Create foreign server context menu at database,
|
||||
* foreign server collections and foreign server node
|
||||
*/
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_foreign_server_on_coll', node: 'coll-foreign_server', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_foreign_server', node: 'foreign_server', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_foreign_server', node: 'foreign_data_wrapper', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
// Defining model for foreign server node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
name: undefined,
|
||||
fsrvtype: undefined,
|
||||
fsrvversion: undefined,
|
||||
fsrvvalue: undefined,
|
||||
fsrvoptions: [],
|
||||
fsrvowner: undefined,
|
||||
description: undefined,
|
||||
fsrvacl: []
|
||||
},
|
||||
|
||||
// Default values!
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
|
||||
this.set({'fsrvowner': userInfo.name}, {silent: true});
|
||||
}
|
||||
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
// Defining schema for the foreign server node
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
type: 'text', disabled: function(m) {
|
||||
if (this.mode == 'edit' && this.node_info.server.version < 90200) {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
},{
|
||||
id: 'fsrvid', label:'{{ _('OID') }}', cell: 'string',
|
||||
type: 'text', disabled: true, mode: ['properties']
|
||||
},{
|
||||
id: 'fsrvowner', label:'{{ _('Owner') }}', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'role',
|
||||
mode: ['edit', 'create', 'properties'], select2: { allowClear: false }
|
||||
},{
|
||||
id: 'fsrvtype', label:'{{ _('Type') }}', cell: 'string',
|
||||
group: '{{ _('Definition') }}', type: 'text', mode: ['edit','create','properties'], disabled: function(m) {
|
||||
return !m.isNew();
|
||||
}
|
||||
},{
|
||||
id: 'fsrvversion', label:'{{ _('Version') }}', cell: 'string',
|
||||
group: '{{ _('Definition') }}', type: 'text'
|
||||
},{
|
||||
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
|
||||
type: 'multiline'
|
||||
},{
|
||||
id: 'fsrvoptions', label: 'Options', type: 'collection', group: "Options",
|
||||
model: OptionsModel, control: 'unique-col-collection', mode: ['edit', 'create'],
|
||||
canAdd: true, canDelete: true, uniqueCol : ['fsrvoption'],
|
||||
columns: ['fsrvoption','fsrvvalue']
|
||||
},{
|
||||
id: 'fsrvacl', label: 'Privileges', type: 'collection', group: '{{ _('Security') }}',
|
||||
model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), control: 'unique-col-collection',
|
||||
mode: ['edit', 'create'], canAdd: true, canDelete: true, uniqueCol : ['grantee']
|
||||
},{
|
||||
id: 'acl', label: '{{ _('Privileges') }}', type: 'text',
|
||||
group: '{{ _('Security') }}', mode: ['properties'], disabled: true
|
||||
}
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
var name = this.get('name');
|
||||
|
||||
if (_.isUndefined(name) || _.isNull(name) ||
|
||||
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Name can not be empty!') }}';
|
||||
this.errorModel.set('name', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('name');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['coll-foreign_server'];
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
CASE d.privilege_type
|
||||
WHEN 'USAGE' THEN 'U'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS privilege_type
|
||||
FROM
|
||||
(SELECT srvacl FROM pg_foreign_server fsrv
|
||||
LEFT OUTER JOIN pg_shdescription descr ON (
|
||||
fsrv.oid=descr.objoid AND descr.classoid='pg_foreign_server'::regclass)
|
||||
{% if fsid %}
|
||||
WHERE fsrv.oid = {{ fsid|qtLiteral }}::OID
|
||||
{% endif %}
|
||||
) acl,
|
||||
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable,
|
||||
(d).privilege_type AS privilege_type
|
||||
FROM (SELECT aclexplode(srvacl) as d FROM pg_foreign_server fsrv1
|
||||
{% if fsid %}
|
||||
WHERE fsrv1.oid = {{ fsid|qtLiteral }}::OID ) a
|
||||
{% endif %}
|
||||
) d
|
||||
) d
|
||||
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||
GROUP BY g.rolname, gt.rolname
|
|
@ -0,0 +1,40 @@
|
|||
{# ============= Create foreign server ============= #}
|
||||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data %}
|
||||
CREATE SERVER {{ conn|qtIdent(data.name) }}{% if data.fsrvtype %}
|
||||
|
||||
TYPE {{ data.fsrvtype|qtLiteral }}{% endif %}{% if data.fsrvversion %}
|
||||
|
||||
VERSION {{ data.fsrvversion|qtLiteral }}{%-endif %}{% if fdwdata %}
|
||||
|
||||
FOREIGN DATA WRAPPER {{ conn|qtIdent(fdwdata.name) }}{% endif %}{% if data.fsrvoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
{{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}{% if data %};{% endif %}
|
||||
|
||||
{# ============= Set the owner for foreign server ============= #}
|
||||
{% if data.fsrvowner %}
|
||||
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
|
||||
{% endif %}
|
||||
{# ============= Set the comment for foreign server ============= #}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Set the ACL for foreign server ============= #}
|
||||
{% if data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,9 @@
|
|||
{# ============= Give foreign server name from foreign server id ============= #}
|
||||
{% if fsid %}
|
||||
SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
|
||||
WHERE srv.oid={{fsid}}::int;
|
||||
{% endif %}
|
||||
{# ============= Delete/Drop cascade foreign server ============= #}
|
||||
{% if name %}
|
||||
DROP SERVER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
|
||||
{% endif %}
|
|
@ -0,0 +1,12 @@
|
|||
{# ============= Get dependents of foreign server ============= #}
|
||||
{% if fsid %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser WHERE u.umserver = {{ fsid }}::OID
|
||||
)
|
||||
|
||||
SELECT um.um_oid, name, dep.deptype FROM umapData um
|
||||
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
|
||||
{% endif %}
|
|
@ -0,0 +1,27 @@
|
|||
{# ============= Give all the properties of foreign server ============= #}
|
||||
{% if fdwid %}
|
||||
SELECT fdw.oid as fdwoid,
|
||||
fdwname as name
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
WHERE fdw.oid={{fdwid}}::int
|
||||
{% else %}
|
||||
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
|
||||
array_to_string(srvoptions, ',') AS fsrvoptions,
|
||||
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
|
||||
FROM pg_foreign_server srv
|
||||
LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
|
||||
{% if data and fdwdata %}
|
||||
WHERE fdw.fdwname = {{ fdwdata.name|qtLiteral }}::text and srvname = {{ data.name|qtLiteral }}::text
|
||||
{% elif fdwdata %}
|
||||
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
|
||||
{% endif %}
|
||||
{% if fid %}
|
||||
WHERE srvfdw={{fid}}::int
|
||||
{% endif %}
|
||||
{% if fsid %}
|
||||
WHERE srv.oid={{fsid}}::int
|
||||
{% endif %}
|
||||
ORDER BY srvname;
|
||||
{% endif %}
|
|
@ -0,0 +1,87 @@
|
|||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data %}
|
||||
{# ============= Update foreign server name ============= #}
|
||||
{% if data.name != o_data.name %}
|
||||
ALTER SERVER {{ conn|qtIdent(o_data.name) }}
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server owner ============= #}
|
||||
{% if data.fsrvowner and data.fsrvowner != o_data.fsrvowner %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server version ============= #}
|
||||
{% if data.fsrvversion and data.fsrvversion != o_data.fsrvversion %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
VERSION {{ data.fsrvversion|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server comments ============= #}
|
||||
{% if data.description and data.description != o_data.description %}
|
||||
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server options and values ============= #}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.deleted %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.deleted %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
DROP {{conn|qtIdent(variable.fsrvoption)}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.added %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.added %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
ADD {{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.changed %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.changed %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
SET {{conn|qtIdent(variable.fsrvoption)}} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{# Change the privileges #}
|
||||
{% if data.fsrvacl %}
|
||||
{% if 'deleted' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.deleted %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'changed' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.changed %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'added' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.added %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
|
@ -0,0 +1,21 @@
|
|||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
CASE d.privilege_type
|
||||
WHEN 'USAGE' THEN 'U'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS privilege_type
|
||||
FROM
|
||||
(SELECT srvacl FROM pg_foreign_server fsrv
|
||||
LEFT OUTER JOIN pg_shdescription descr ON (
|
||||
fsrv.oid=descr.objoid AND descr.classoid='pg_foreign_server'::regclass)
|
||||
{% if fsid %}
|
||||
WHERE fsrv.oid = {{ fsid|qtLiteral }}::OID
|
||||
{% endif %}
|
||||
) acl,
|
||||
aclexplode(srvacl) d
|
||||
) d
|
||||
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||
GROUP BY g.rolname, gt.rolname
|
|
@ -0,0 +1,40 @@
|
|||
{# ============= Create foreign server ============= #}
|
||||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data.name %}
|
||||
CREATE SERVER {{ conn|qtIdent(data.name) }}{% if data.fsrvtype %}
|
||||
|
||||
TYPE {{ data.fsrvtype|qtLiteral }}{% endif %}{% if data.fsrvversion %}
|
||||
|
||||
VERSION {{ data.fsrvversion|qtLiteral }}{%-endif %}{% if fdwdata %}
|
||||
|
||||
FOREIGN DATA WRAPPER {{ conn|qtIdent(fdwdata.name) }}{% endif %}{% if data.fsrvoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
{{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}{% if data %};{% endif %}
|
||||
|
||||
{# ============= Set the owner for foreign server ============= #}
|
||||
{% if data.fsrvowner %}
|
||||
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
|
||||
{% endif %}
|
||||
{# ============= Set the comment for foreign server ============= #}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Set the ACL for foreign server ============= #}
|
||||
{% if data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,9 @@
|
|||
{# ============= Give foreign server name from foreign server id ============= #}
|
||||
{% if fsid %}
|
||||
SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
|
||||
WHERE srv.oid={{fsid}}::int;
|
||||
{% endif %}
|
||||
{# ============= Delete/Drop cascade foreign server ============= #}
|
||||
{% if name %}
|
||||
DROP SERVER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
|
||||
{% endif %}
|
|
@ -0,0 +1,12 @@
|
|||
{# ============= Get dependents of foreign server ============= #}
|
||||
{% if fsid %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser WHERE u.umserver = {{ fsid }}::OID
|
||||
)
|
||||
|
||||
SELECT um.um_oid, name, dep.deptype FROM umapData um
|
||||
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
|
||||
{% endif %}
|
|
@ -0,0 +1,26 @@
|
|||
{# ============= Give all the properties of foreign server ============= #}
|
||||
{% if fdwid %}
|
||||
SELECT fdw.oid as fdwoid, fdwname as name
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
WHERE fdw.oid={{fdwid}}::int
|
||||
{% else %}
|
||||
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
|
||||
array_to_string(srvoptions, ',') AS fsrvoptions,
|
||||
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
|
||||
FROM pg_foreign_server srv
|
||||
LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
|
||||
{% if data and fdwdata %}
|
||||
WHERE fdw.fdwname = {{ fdwdata.name|qtLiteral }}::text and srvname = {{ data.name|qtLiteral }}::text
|
||||
{% elif fdwdata %}
|
||||
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
|
||||
{% endif %}
|
||||
{% if fid %}
|
||||
WHERE srvfdw={{fid}}::int
|
||||
{% endif %}
|
||||
{% if fsid %}
|
||||
WHERE srv.oid={{fsid}}::int
|
||||
{% endif %}
|
||||
ORDER BY srvname;
|
||||
{% endif %}
|
|
@ -0,0 +1,87 @@
|
|||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data %}
|
||||
{# ============= Update foreign server name ============= #}
|
||||
{% if data.name != o_data.name %}
|
||||
ALTER SERVER {{ conn|qtIdent(o_data.name) }}
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server owner ============= #}
|
||||
{% if data.fsrvowner and data.fsrvowner != o_data.fsrvowner %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server version ============= #}
|
||||
{% if data.fsrvversion and data.fsrvversion != o_data.fsrvversion %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
VERSION {{ data.fsrvversion|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server comments ============= #}
|
||||
{% if data.description and data.description != o_data.description %}
|
||||
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign server options and values ============= #}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.deleted %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.deleted %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
DROP {{conn|qtIdent(variable.fsrvoption)}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.added %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.added %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
ADD {{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fsrvoptions and data.fsrvoptions.changed %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fsrvoptions.changed %}
|
||||
{% if variable.fsrvoption and variable.fsrvoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER SERVER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
SET {{conn|qtIdent(variable.fsrvoption)}} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{# Change the privileges #}
|
||||
{% if data.fsrvacl %}
|
||||
{% if 'deleted' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.deleted %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'changed' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.changed %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'added' in data.fsrvacl %}
|
||||
{% for priv in data.fsrvacl.added %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
|
@ -0,0 +1,753 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Implements User Mapping Node"""
|
||||
|
||||
import json
|
||||
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 as servers
|
||||
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 UserMappingModule(CollectionNodeModule):
|
||||
"""
|
||||
class UserMappingModule(CollectionNodeModule)
|
||||
|
||||
A module class for user mapping node derived from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the user mapping module and it's base module.
|
||||
|
||||
* get_nodes(gid, sid, did, fid, fsid)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* node_inode()
|
||||
- Method is overridden from its base class to make the node as leaf node.
|
||||
|
||||
* script_load(self)
|
||||
- Load the module script for user mapping, when any of the foreign server node is
|
||||
initialized.
|
||||
"""
|
||||
|
||||
NODE_TYPE = 'user_mapping'
|
||||
COLLECTION_LABEL = gettext("User Mappings")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Method is used to initialize the User mapping module and it's base module.
|
||||
|
||||
Args:
|
||||
*args:
|
||||
**kwargs:
|
||||
"""
|
||||
|
||||
self.min_ver = None
|
||||
self.max_ver = None
|
||||
|
||||
super(UserMappingModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_nodes(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
Method is used to generate the browser collection node
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
yield self.generate_browser_collection_node(fsid)
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
"""
|
||||
node_inode
|
||||
|
||||
Override this property to make the node as leaf node.
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for user mapping, when any of the foreign server node is initialized.
|
||||
|
||||
Returns: node type of the server module.
|
||||
"""
|
||||
|
||||
return servers.ServerModule.NODE_TYPE
|
||||
|
||||
blueprint = UserMappingModule(__name__)
|
||||
|
||||
|
||||
class UserMappingView(PGChildNodeView):
|
||||
"""
|
||||
class UserMappingView(PGChildNodeView)
|
||||
|
||||
A view class for user mapping node derived from PGChildNodeView. This class is
|
||||
responsible for all the stuff related to view like updating user mapping
|
||||
node, showing properties, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the UserMappingView 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(gid, sid, did, fid, fsid)
|
||||
- This function is used to list all the user mapping nodes within that collection.
|
||||
|
||||
* nodes(gid, sid, did, fid, fsid)
|
||||
- This function will used to create all the child node within that collection.
|
||||
Here it will create all the user mapping node.
|
||||
|
||||
* properties(gid, sid, did, fid, fsid, umid)
|
||||
- This function will show the properties of the selected user mapping node
|
||||
|
||||
* tokenizeOptions(option_value)
|
||||
- This function will tokenize the string stored in database
|
||||
|
||||
* update(gid, sid, did, fid, fsid, umid)
|
||||
- This function will update the data for the selected user mapping node
|
||||
|
||||
* create(gid, sid, did, fid, fsid)
|
||||
- This function will create the new user mapping node
|
||||
|
||||
* delete(gid, sid, did, fid, fsid, umid)
|
||||
- This function will delete the selected user mapping node
|
||||
|
||||
* msql(gid, sid, did, fid, fsid, umid)
|
||||
- This function is used to return modified SQL for the selected user mapping node
|
||||
|
||||
* get_sql(data, fid, fsid, umid)
|
||||
- This function will generate sql from model data
|
||||
|
||||
* sql(gid, sid, did, fid, fsid, umid):
|
||||
- This function will generate sql to show it in sql pane for the selected user mapping node.
|
||||
|
||||
* dependents(gid, sid, did, fid, fsid, umid):
|
||||
- This function get the dependents and return ajax response for the user mapping node.
|
||||
|
||||
* dependencies(self, gid, sid, did, fid, fsid, umid):
|
||||
- This function get the dependencies and return ajax response for the user mapping node.
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'fid'},
|
||||
{'type': 'int', 'id': 'fsid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'umid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'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'}]
|
||||
})
|
||||
|
||||
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(
|
||||
"user_mappings/js/user_mappings.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!"
|
||||
)
|
||||
)
|
||||
|
||||
self.template_path = 'user_mappings/sql/9.1_plus'
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90100:
|
||||
self.template_path = 'user_mappings/sql/9.1_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function is used to list all the user mapping nodes within that collection.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
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, fid, fsid):
|
||||
"""
|
||||
This function will used to create all the child node within that collection.
|
||||
Here it will create all the user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
res = []
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['um_oid'],
|
||||
fsid,
|
||||
row['name'],
|
||||
icon="icon-user_mapping"
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
def tokenizeOptions(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
e.g. database store the value as below
|
||||
key1=value1, key2=value2, key3=value3, ....
|
||||
This function will extract key and value from above string
|
||||
|
||||
Args:
|
||||
option_value: key value option/value pair read from database
|
||||
"""
|
||||
|
||||
if option_value is not None:
|
||||
option_str = option_value.split(',')
|
||||
um_options = []
|
||||
for um_option in option_str:
|
||||
k, v = um_option.split('=', 1)
|
||||
um_options.append({'umoption': k,'umvalue': v})
|
||||
return um_options
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function will show the properties of the selected user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['name'] == 'public':
|
||||
res['rows'][0]['name'] = 'PUBLIC'
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will create the user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Couldn't find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
new_list = []
|
||||
|
||||
if 'umoptions' in data:
|
||||
for item in data['umoptions']:
|
||||
new_dict = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['umoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_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, 'properties.sql']), fsid=fsid, data=data,
|
||||
conn=self.conn)
|
||||
status, r_set = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['um_oid'],
|
||||
fsid,
|
||||
row['name'],
|
||||
icon='icon-user_mapping'
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function will update the data for the selected user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
try:
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="User Mapping updated",
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function will delete the selected user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign server from fsid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
data = res['rows'][0]
|
||||
|
||||
# drop user mapping
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), data=data, name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("User Mapping dropped"),
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, fid, fsid, umid=None):
|
||||
"""
|
||||
This function is used to return modified SQL for the selected user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data='-- Modified SQL --',
|
||||
status=200
|
||||
)
|
||||
|
||||
def get_sql(self, gid, sid, data, did, fid, fsid, umid=None):
|
||||
"""
|
||||
This function will generate sql from model data.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
data: Contains the data of the selected user mapping node
|
||||
fid: foreign data wrapper ID
|
||||
fsid: foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if umid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'added' in data['umoptions']:
|
||||
for item in data['umoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
data['umoptions']['added'] = new_list_add
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'changed' in data['umoptions']:
|
||||
for item in data['umoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
data['umoptions']['changed'] = new_list_change
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
fdwdata=fdw_data, conn=self.conn)
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
fdw_data = res['rows'][0]
|
||||
|
||||
new_list = []
|
||||
|
||||
if 'umoptions' in data:
|
||||
for item in data['umoptions']:
|
||||
new_dict = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] \
|
||||
and item['umvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update(
|
||||
{'umoption': item['umoption'],
|
||||
'umvalue': ''}
|
||||
)
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['umoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function will generate sql to show it in sql pane for the selected user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
sql = ''
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
|
||||
sql_header = """-- User Mapping : {0}
|
||||
|
||||
-- DROP USER MAPPING FOR {0} SERVER {1}
|
||||
|
||||
""".format(res['rows'][0]['name'], fdw_data['name'])
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
return ajax_response(response=sql)
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function get the dependents and return ajax response
|
||||
for the user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: user mapping ID
|
||||
"""
|
||||
|
||||
dependents_result = self.get_dependents(self.conn, umid)
|
||||
return ajax_response(
|
||||
response=dependents_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def dependencies(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function get the dependencies and return ajax response
|
||||
for the user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign Data Wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: user mapping ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(self.conn, umid)
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
UserMappingView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 557 B |
Binary file not shown.
After Width: | Height: | Size: 518 B |
|
@ -0,0 +1,157 @@
|
|||
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 Options model
|
||||
var OptionsModel = pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
umoption: undefined,
|
||||
umvalue: undefined
|
||||
},
|
||||
|
||||
// Defining schema for the Options model
|
||||
schema: [
|
||||
{id: 'umoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true},
|
||||
{id: 'umvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true},
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
// Validation for the option value
|
||||
if (_.isUndefined(this.get('umoption')) ||
|
||||
_.isNull(this.get('umoption')) ||
|
||||
String(this.get('umoption')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please enter an option name';
|
||||
this.errorModel.set('umoption', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('umoption');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Extend the browser's collection class for user mapping collection
|
||||
if (!pgBrowser.Nodes['coll-user_mapping']) {
|
||||
var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-user_mapping'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'user_mapping',
|
||||
label: '{{ _('User Mappings') }}',
|
||||
type: 'coll-user_mapping',
|
||||
columns: ['name']
|
||||
});
|
||||
};
|
||||
|
||||
// Extend the browser's node class for user mapping node
|
||||
if (!pgBrowser.Nodes['user_mapping']) {
|
||||
pgAdmin.Browser.Nodes['user_mapping'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'foreign_server',
|
||||
type: 'user_mapping',
|
||||
label: '{{ _('User Mapping') }}',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
Init: function() {
|
||||
|
||||
// Avoid multiple registration of menus
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
/* Create foreign server context menu at database,
|
||||
* user mapping collections and user mapping node
|
||||
*/
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_user_mapping_on_coll', node: 'coll-user_mapping', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('User Mapping...') }}',
|
||||
icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_user_mapping', node: 'user_mapping', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('User Mapping...') }}',
|
||||
icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_user_mapping', node: 'foreign_server', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('User Mapping...') }}',
|
||||
icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'}
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
// Defining model for user mapping node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
name: undefined,
|
||||
um_options: []
|
||||
},
|
||||
|
||||
// Default values!
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
|
||||
this.set({'name': userInfo.name}, {silent: true});
|
||||
}
|
||||
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
// Defining schema for the user mapping node
|
||||
schema: [{
|
||||
id: 'name', label:'{{ _('User') }}', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'role',
|
||||
mode: ['edit', 'create', 'properties'], select2: { allowClear: false },
|
||||
disabled: function(m) { return !m.isNew(); },
|
||||
transform: function(data) {
|
||||
var self = this;
|
||||
node = self.field.get('schema_node');
|
||||
var res =
|
||||
Backform.NodeListByNameControl.prototype.defaults.transform.apply(
|
||||
this, arguments
|
||||
);
|
||||
res.unshift({label: 'CURRENT_USER', value: 'CURRENT_USER', image: 'icon-' + node.type},
|
||||
{label: 'PUBLIC', value: 'PUBLIC', image: 'icon-' + node.type});
|
||||
return res;
|
||||
}
|
||||
},{
|
||||
id: 'um_oid', label:'{{ _('OID') }}', cell: 'string',
|
||||
type: 'text', disabled: true, mode: ['properties'],
|
||||
},{
|
||||
id: 'umoptions', label: 'Options', type: 'collection', group: "Options",
|
||||
model: OptionsModel, control: 'unique-col-collection', mode: ['create', 'edit'],
|
||||
canAdd: true, canDelete: true, uniqueCol : ['umoption']
|
||||
}
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
var name = this.get('name');
|
||||
|
||||
if (_.isUndefined(name) || _.isNull(name) ||
|
||||
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Name can not be empty!') }}';
|
||||
this.errorModel.set('name', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('name');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['coll-user_mapping'];
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
{# ============= Create of user mapping for server ============= #}
|
||||
{% if data and fdwdata %}
|
||||
CREATE USER MAPPING FOR {% if data.name == "CURRENT_USER" or data.name == "PUBLIC" %}{{ data.name }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} SERVER {{ conn|qtIdent(fdwdata.name) }}{%endif%}{% if data.umoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.umoptions %}
|
||||
{% if variable.umoption and variable.umoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
{{ conn|qtIdent(variable.umoption) }} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{%else%}){% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}{% if data %};{% endif %}
|
|
@ -0,0 +1,9 @@
|
|||
{# ============= Get the foreing server name from id ============= #}
|
||||
{% if fsid %}
|
||||
SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
|
||||
WHERE srv.oid={{fsid}}::int;
|
||||
{% endif %}
|
||||
{# ============= Drop/Delete cascade user mapping ============= #}
|
||||
{% if name and data %}
|
||||
DROP USER MAPPING FOR {{ conn|qtIdent(data.name) }} SERVER {{ conn|qtIdent(name) }}
|
||||
{% endif %}
|
|
@ -0,0 +1,23 @@
|
|||
{# ============= Get the properties of user mapping ============= #}
|
||||
{% if fserid %}
|
||||
SELECT srv.oid as fsrvid, srvname as name
|
||||
FROM pg_foreign_server srv
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
|
||||
WHERE srv.oid = {{fserid}}::int
|
||||
{% endif %}
|
||||
{% if fsid or umid or fdwdata or data %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser {% if fsid %} WHERE u.umserver = {{fsid}}::int {% endif %} {% if umid %} WHERE u.oid= {{umid}}::int {% endif %}
|
||||
)
|
||||
SELECT * FROM umapData
|
||||
{% if data %}
|
||||
WHERE {% if data.name == "CURRENT_USER" %} name = {{data.name}} {% elif data.name == "PUBLIC" %} name = {{data.name.lower()|qtLiteral}} {% else %} name = {{data.name|qtLiteral}} {% endif %}
|
||||
{% endif %}
|
||||
{% if fdwdata %}
|
||||
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
|
||||
{% endif %}
|
||||
ORDER BY 2;
|
||||
{% endif %}
|
|
@ -0,0 +1,37 @@
|
|||
{# ============= Update user mapping options and values ============= #}
|
||||
{% if data.umoptions and data.umoptions.deleted and fdwdata %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.umoptions.deleted %}
|
||||
{% if variable.umoption and variable.umoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
DROP {{conn|qtIdent(variable.umoption)}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% if o_data.name and data.umoptions and data.umoptions.added and fdwdata %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.umoptions.added %}
|
||||
{% if variable.umoption and variable.umoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
ADD {{ conn|qtIdent(variable.umoption) }} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% if data.umoptions and data.umoptions.changed and fdwdata %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.umoptions.changed %}
|
||||
{% if variable.umoption and variable.umoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
SET {{conn|qtIdent(variable.umoption)}} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{%endif%}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
Binary file not shown.
After Width: | Height: | Size: 444 B |
Binary file not shown.
After Width: | Height: | Size: 411 B |
|
@ -0,0 +1,180 @@
|
|||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
|
||||
'alertify', 'pgadmin.browser.collection',
|
||||
'pgadmin.browser.server.privilege'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// Extend the browser's node model class to create a Options model
|
||||
var OptionsModel = pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
fdwoption: undefined,
|
||||
fdwvalue: undefined
|
||||
},
|
||||
|
||||
// Defining schema for the Options model
|
||||
schema: [
|
||||
{id: 'fdwoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true},
|
||||
{id: 'fdwvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true},
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
// Validation for the option name
|
||||
if (_.isUndefined(this.get('fdwoption')) ||
|
||||
_.isNull(this.get('fdwoption')) ||
|
||||
String(this.get('fdwoption')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please enter an option name';
|
||||
this.errorModel.set('fdwoption', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('fdwoption');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Extend the browser's collection class for foreign data wrapper collection
|
||||
if (!pgBrowser.Nodes['coll-foreign_data_wrapper']) {
|
||||
var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-foreign_data_wrapper'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'foreign_data_wrapper',
|
||||
label: '{{ _('Foreign Data Wrappers') }}',
|
||||
type: 'coll-foreign_data_wrapper',
|
||||
columns: ['name','fdwowner','description']
|
||||
});
|
||||
};
|
||||
|
||||
// Extend the browser's node class for foreign data wrapper node
|
||||
if (!pgBrowser.Nodes['foreign_data_wrapper']) {
|
||||
pgAdmin.Browser.Nodes['foreign_data_wrapper'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'database',
|
||||
type: 'foreign_data_wrapper',
|
||||
label: '{{ _('Foreign Data Wrapper') }}',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
Init: function() {
|
||||
|
||||
// Avoid multiple registration of menus
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
/* Create foreign data wrapper context menu at database,
|
||||
* foreign data wrapper collections and foreign data wrapper node
|
||||
*/
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_foreign_data_wrapper_on_coll', node: 'coll-foreign_data_wrapper', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_foreign_data_wrapper', node: 'foreign_data_wrapper', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_foreign_data_wrapper', node: 'database', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}',
|
||||
icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'}
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
// Defining model for foreign data wrapper node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
name: undefined,
|
||||
fdwowner: undefined,
|
||||
comment: undefined,
|
||||
fdwvalue: undefined,
|
||||
fdwhan: undefined,
|
||||
fdwoption: undefined,
|
||||
fdwacl: []
|
||||
},
|
||||
|
||||
// Default values!
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
|
||||
this.set({'fdwowner': userInfo.name}, {silent: true});
|
||||
}
|
||||
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
// Defining schema for the foreign data wrapper node
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
type: 'text', disabled: function(m) {
|
||||
|
||||
// name field will be disabled only if edit mode and server version is below 9.2
|
||||
if (this.mode == 'edit' && this.node_info.server.version < 90200) {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
},{
|
||||
id: 'fdwoid', label:'{{ _('OID') }}', cell: 'string',
|
||||
type: 'text', disabled: true, mode: ['properties']
|
||||
},{
|
||||
id: 'fdwowner', label:'{{ _('Owner') }}', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'role',
|
||||
mode: ['edit', 'create', 'properties'], select2: { allowClear: false }
|
||||
},{
|
||||
id: 'fdwhan', label:'{{ _('Handler') }}', type: 'text', control: 'node-ajax-options',
|
||||
group: '{{ _('Definition') }}', mode: ['edit', 'create', 'properties'], url:'get_handlers'
|
||||
},{
|
||||
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
|
||||
type: 'multiline'
|
||||
},{
|
||||
id: 'fdwoptions', label: 'Options', type: 'collection', group: '{{ _('Options') }}',
|
||||
model: OptionsModel, control: 'unique-col-collection', mode: ['edit', 'create'],
|
||||
canAdd: true, canDelete: true, uniqueCol : ['fdwoption'],
|
||||
columns: ['fdwoption','fdwvalue']
|
||||
},{
|
||||
id: 'fdwvalue', label:'{{ _('Validator') }}', type: 'text', control: 'node-ajax-options',
|
||||
group: '{{ _('Definition') }}', mode: ['edit', 'create', 'properties'], url:'get_validators'
|
||||
},{
|
||||
id: 'fdwacl', label: 'Privileges', type: 'collection', group: '{{ _('Security') }}',
|
||||
model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), control: 'unique-col-collection',
|
||||
mode: ['edit', 'create'], canAdd: true, canDelete: true, uniqueCol : ['grantee']
|
||||
},{
|
||||
id: 'acl', label: '{{ _('Privileges') }}', type: 'text',
|
||||
group: '{{ _('Security') }}', mode: ['properties'], disabled: true
|
||||
}
|
||||
],
|
||||
|
||||
/* validate function is used to validate the input given by
|
||||
* the user. In case of error, message will be displayed on
|
||||
* the browser for the respective control.
|
||||
*/
|
||||
validate: function() {
|
||||
var name = this.get('name');
|
||||
|
||||
if (_.isUndefined(name) || _.isNull(name) ||
|
||||
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Name can not be empty!') }}';
|
||||
this.errorModel.set('name', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('name');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['coll-foreign_data_wrapper'];
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
CASE d.privilege_type
|
||||
WHEN 'USAGE' THEN 'U'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS privilege_type
|
||||
FROM
|
||||
(SELECT fdwacl FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_shdescription descr ON (
|
||||
fdw.oid=descr.objoid AND descr.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid = {{ fid|qtLiteral }}::OID
|
||||
{% endif %}
|
||||
) acl,
|
||||
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable,
|
||||
(d).privilege_type AS privilege_type
|
||||
FROM (SELECT aclexplode(fdwacl) as d FROM pg_foreign_data_wrapper fdw1
|
||||
{% if fid %}
|
||||
WHERE fdw1.oid = {{ fid|qtLiteral }}::OID ) a
|
||||
{% endif %}
|
||||
) d
|
||||
) d
|
||||
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||
GROUP BY g.rolname, gt.rolname
|
|
@ -0,0 +1,38 @@
|
|||
{# ============= Create foreign data wrapper ============= #}
|
||||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data.name %}
|
||||
CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %}
|
||||
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %}
|
||||
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
{{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}{% endif %}{%if data %};{%endif%}
|
||||
|
||||
{# ============= Set the owner for foreign data wrapper ============= #}
|
||||
{% if data.fdwowner %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fdwowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Comment on of foreign data wrapper object ============= #}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Create ACL for foreign data wrapper ============= #}
|
||||
{% if data.fdwacl %}
|
||||
{% for priv in data.fdwacl %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,8 @@
|
|||
{# ============= Get foreign data wrapper from fid ============= #}
|
||||
{% if fid %}
|
||||
SELECT fdwname as name from pg_foreign_data_wrapper WHERE oid={{fid}}::int;
|
||||
{% endif %}
|
||||
{# ============= Delete/Drop cascade foreign data wrapper ============= #}
|
||||
{% if name %}
|
||||
DROP FOREIGN DATA WRAPPER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
|
||||
{% endif %}
|
|
@ -0,0 +1,2 @@
|
|||
{# ============= Get the handlers of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115;
|
|
@ -0,0 +1,14 @@
|
|||
{# ============= Get all the properties of foreign data wrapper ============= #}
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler
|
||||
LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid={{fid}}::int
|
||||
{% endif %}
|
||||
{% if fname %}
|
||||
WHERE fdw.fdwname={{ fname|qtLiteral }}::text
|
||||
{% endif %}
|
||||
ORDER BY fdwname
|
|
@ -0,0 +1,99 @@
|
|||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data %}
|
||||
{# ============= Update foreign data wrapper name ============= #}
|
||||
{% if data.name != o_data.name %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(o_data.name) }}
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper owner ============= #}
|
||||
{% if data.fdwowner and data.fdwowner != o_data.fdwowner %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fdwowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper validator ============= #}
|
||||
{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
NO VALIDATOR;
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper handler ============= #}
|
||||
{% if data.fdwhan and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
NO HANDLER;
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper comments ============= #}
|
||||
{% if data.description and data.description != o_data.description %}
|
||||
COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper options and values ============= #}
|
||||
{% if data.fdwoptions and data.fdwoptions.deleted %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.deleted %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
DROP {{ conn|qtIdent(variable.fdwoption) }}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwoptions and data.fdwoptions.added %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.added %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
ADD {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwoptions and data.fdwoptions.changed %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.changed %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
SET {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{# Change the privileges #}
|
||||
{% if data.fdwacl %}
|
||||
{% if 'deleted' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.deleted %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'changed' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.changed %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'added' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.added %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,2 @@
|
|||
{# ============= Get the validators of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
|
@ -0,0 +1,21 @@
|
|||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
CASE d.privilege_type
|
||||
WHEN 'USAGE' THEN 'U'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS privilege_type
|
||||
FROM
|
||||
(SELECT fdwacl FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_shdescription descr ON (
|
||||
fdw.oid=descr.objoid AND descr.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid = {{ fid|qtLiteral }}::OID
|
||||
{% endif %}
|
||||
) acl,
|
||||
aclexplode(fdwacl) d
|
||||
) d
|
||||
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||
GROUP BY g.rolname, gt.rolname
|
|
@ -0,0 +1,37 @@
|
|||
{# ============= Create foreign data wrapper ============= #}
|
||||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data.name %}
|
||||
CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %}
|
||||
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %}
|
||||
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
{{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}{% endif %}{%if data %};{%endif%}
|
||||
|
||||
{# ============= Set the owner for foreign data wrapper ============= #}
|
||||
{% if data.fdwowner %}
|
||||
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fdwowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Comment on of foreign data wrapper object ============= #}
|
||||
{% if data.description %}
|
||||
COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Create ACL for foreign data wrapper ============= #}
|
||||
{% if data.fdwacl %}
|
||||
{% for priv in data.fdwacl %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,8 @@
|
|||
{# ============= Get foreign data wrapper from fid ============= #}
|
||||
{% if fid %}
|
||||
SELECT fdwname as name from pg_foreign_data_wrapper WHERE oid={{fid}}::int;
|
||||
{% endif %}
|
||||
{# ============= Delete/Drop cascade foreign data wrapper ============= #}
|
||||
{% if name %}
|
||||
DROP FOREIGN DATA WRAPPER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
|
||||
{% endif %}
|
|
@ -0,0 +1,2 @@
|
|||
{# ============= Get the handlers of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115;
|
|
@ -0,0 +1,14 @@
|
|||
{# ============= Get all the properties of foreign data wrapper ============= #}
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler
|
||||
LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid={{fid}}::int
|
||||
{% endif %}
|
||||
{% if fname %}
|
||||
WHERE fdw.fdwname={{ fname|qtLiteral }}::text
|
||||
{% endif %}
|
||||
ORDER BY fdwname
|
|
@ -0,0 +1,99 @@
|
|||
{% import 'macros/privilege.macros' as PRIVILEGE %}
|
||||
{% if data %}
|
||||
{# ============= Update foreign data wrapper name ============= #}
|
||||
{% if data.name != o_data.name %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(o_data.name) }}
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper owner ============= #}
|
||||
{% if data.fdwowner and data.fdwowner != o_data.fdwowner %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.fdwowner) }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper validator ============= #}
|
||||
{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
NO VALIDATOR;
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper handler ============= #}
|
||||
{% if data.fdwhan and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
NO HANDLER;
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper comments ============= #}
|
||||
{% if data.description and data.description != o_data.description %}
|
||||
COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# ============= Update foreign data wrapper options and values ============= #}
|
||||
{% if data.fdwoptions and data.fdwoptions.deleted %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.deleted %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{%endif%}
|
||||
DROP {{ conn|qtIdent(variable.fdwoption) }}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwoptions and data.fdwoptions.added %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.added %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
ADD {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwoptions and data.fdwoptions.changed %}
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions.changed %}
|
||||
{% if variable.fdwoption and variable.fdwoption != '' %}
|
||||
{% if addAlter == "False" %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
OPTIONS ({% set addAlter = "True" %}{% endif %}
|
||||
SET {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{# Change the privileges #}
|
||||
{% if data.fdwacl %}
|
||||
{% if 'deleted' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.deleted %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }} {% endfor %}
|
||||
{% endif %}
|
||||
{% if 'changed' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.changed %}
|
||||
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} {% endfor %}
|
||||
{% endif %}
|
||||
{% if 'added' in data.fdwacl %}
|
||||
{% for priv in data.fdwacl.added %}
|
||||
{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,2 @@
|
|||
{# ============= Get the validators of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
|
@ -8,11 +8,14 @@ SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
|
|||
WHEN la.oid IS NOT NULL THEN 'l'::text
|
||||
WHEN rw.oid IS NOT NULL THEN 'R'::text
|
||||
WHEN co.oid IS NOT NULL THEN 'C'::text || contype
|
||||
WHEN ad.oid IS NOT NULL THEN 'A'::text ELSE ''
|
||||
WHEN ad.oid IS NOT NULL THEN 'A'::text
|
||||
WHEN fs.oid IS NOT NULL THEN 'F'::text
|
||||
WHEN fdw.oid IS NOT NULL THEN 'f'::text
|
||||
ELSE ''
|
||||
END AS type,
|
||||
COALESCE(coc.relname, clrw.relname) AS ownertable,
|
||||
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || '.' || att.attname
|
||||
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname)
|
||||
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname, fs.srvname, fdw.fdwname)
|
||||
END AS refname,
|
||||
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname
|
||||
FROM pg_depend dep
|
||||
|
@ -33,10 +36,12 @@ LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid
|
|||
LEFT JOIN pg_language la ON dep.refobjid=la.oid
|
||||
LEFT JOIN pg_namespace ns ON dep.refobjid=ns.oid
|
||||
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
|
||||
LEFT JOIN pg_foreign_server fs ON fs.oid=dep.refobjid
|
||||
LEFT JOIN pg_foreign_data_wrapper fdw ON fdw.oid=dep.refobjid
|
||||
{{where_clause}} AND
|
||||
refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
|
||||
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
|
||||
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger'))
|
||||
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
|
||||
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper'))
|
||||
ORDER BY refclassid, cl.relkind
|
||||
{% endif %}
|
||||
|
||||
|
@ -57,11 +62,14 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, ad.adsrc,
|
|||
WHEN la.oid IS NOT NULL THEN 'l'::text
|
||||
WHEN rw.oid IS NOT NULL THEN 'R'::text
|
||||
WHEN co.oid IS NOT NULL THEN 'C'::text || contype
|
||||
WHEN ad.oid IS NOT NULL THEN 'A'::text ELSE ''
|
||||
WHEN ad.oid IS NOT NULL THEN 'A'::text
|
||||
WHEN fs.oid IS NOT NULL THEN 'F'::text
|
||||
WHEN fdw.oid IS NOT NULL THEN 'f'::text
|
||||
ELSE ''
|
||||
END AS type,
|
||||
COALESCE(coc.relname, clrw.relname) AS ownertable,
|
||||
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || '.' || att.attname
|
||||
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname)
|
||||
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname, fs.srvname, fdw.fdwname)
|
||||
END AS refname,
|
||||
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname
|
||||
FROM pg_depend dep
|
||||
|
@ -82,9 +90,11 @@ LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid
|
|||
LEFT JOIN pg_language la ON dep.objid=la.oid
|
||||
LEFT JOIN pg_namespace ns ON dep.objid=ns.oid
|
||||
LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid
|
||||
LEFT JOIN pg_foreign_server fs ON fs.oid=dep.objid
|
||||
LEFT JOIN pg_foreign_data_wrapper fdw ON fdw.oid=dep.objid
|
||||
{{where_clause}} AND
|
||||
classid IN ( SELECT oid FROM pg_class WHERE relname IN
|
||||
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
|
||||
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger'))
|
||||
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
|
||||
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper'))
|
||||
ORDER BY classid, cl.relkind
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -426,6 +426,8 @@ class PGChildNodeView(NodeView):
|
|||
'y': 'type',
|
||||
'T': 'trigger',
|
||||
'l': 'language',
|
||||
'f': 'foreign_data_wrapper',
|
||||
'F': 'foreign_server',
|
||||
'R': None,
|
||||
'C': None,
|
||||
'A': None
|
||||
|
|
|
@ -49,7 +49,7 @@ define(
|
|||
(node['node_image']).apply(node, [null, null]) :
|
||||
(node['node_image'] || ('icon-' + res.type))) :
|
||||
('icon-' + res.type);
|
||||
res.type = S.capitalize(res.type, true);
|
||||
res.type = S.titleize(res.type.replace(/_/g, " "), true);
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
@ -82,7 +82,7 @@ define(
|
|||
cell: Backgrid.Cell.extend({
|
||||
render: function() {
|
||||
Backgrid.Cell.prototype.render.apply(this, arguments);
|
||||
this.$el.addClass(this.model.get('icon')).css({"padding-left": "18px"});
|
||||
this.$el.prepend($('<i>', {class: "wcTabIcon " + this.model.get('icon')}));
|
||||
return this;
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -117,6 +117,8 @@ function(alertify, S) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
alertify.alert().show().set('message', msg).set('title', promptmsg);
|
||||
alertify.alert().show().set(
|
||||
'message', msg.replace(new RegExp('\r?\n','g'), '<br />')
|
||||
).set('title', promptmsg);
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue