Add support for extensions.
parent
f466e0169a
commit
c950683fa1
|
@ -0,0 +1,480 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Implements Extension 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.databases as databases
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from functools import wraps
|
||||
|
||||
# As unicode type is not available in python3
|
||||
# If we check a variable is "isinstance(variable, str)
|
||||
# it breaks in python 3 as variable type is not string its unicode.
|
||||
# We assign basestring as str type if it is python3, unicode
|
||||
# if it is python2.
|
||||
|
||||
try:
|
||||
unicode = unicode
|
||||
except NameError:
|
||||
# 'unicode' is undefined, must be Python 3
|
||||
str = str
|
||||
unicode = str
|
||||
bytes = bytes
|
||||
basestring = (str, bytes)
|
||||
else:
|
||||
# 'unicode' exists, must be Python 2
|
||||
str = str
|
||||
unicode = unicode
|
||||
bytes = str
|
||||
basestring = basestring
|
||||
|
||||
|
||||
class ExtensionModule(CollectionNodeModule):
|
||||
"""
|
||||
class ExtensionModule(Object):
|
||||
|
||||
A collection Node which inherits CollectionNodeModule
|
||||
class and define methods to get child nodes, to load its own
|
||||
javascript file.
|
||||
"""
|
||||
NODE_TYPE = "extension"
|
||||
COLLECTION_LABEL = gettext("Extensions")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Initialising the base class
|
||||
"""
|
||||
super(ExtensionModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_nodes(self, gid, sid, did):
|
||||
"""
|
||||
Generate the collection node
|
||||
"""
|
||||
yield self.generate_browser_collection_node(did)
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
"""
|
||||
If a node have child return True otherwise False
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for extension, when any of the database nodes are
|
||||
initialized.
|
||||
"""
|
||||
return databases.DatabaseModule.NODE_TYPE
|
||||
|
||||
|
||||
# Create blueprint of extension module
|
||||
blueprint = ExtensionModule(__name__)
|
||||
|
||||
|
||||
class ExtensionView(PGChildNodeView):
|
||||
"""
|
||||
This is a class for extension nodes which inherits the
|
||||
properties and methods from NodeView class and define
|
||||
various methods to list, create, update and delete extension.
|
||||
|
||||
Variables:
|
||||
---------
|
||||
* node_type - tells which type of node it is
|
||||
* parent_ids - id with its type and name of parent nodes
|
||||
* ids - id with type and name of extension module being used.
|
||||
* operations - function routes mappings defined.
|
||||
"""
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'eid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
'stats': [{'get': 'statistics'}],
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'avails': [{}, {'get': 'avails'}],
|
||||
'schemas': [{}, {'get': 'schemas'}],
|
||||
'children': [{'get': 'children'}]
|
||||
})
|
||||
|
||||
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'])
|
||||
self.template_path = 'extensions/sql'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did):
|
||||
"""
|
||||
Fetches all extensions properties and render into properties tab
|
||||
"""
|
||||
SQL = render_template("/".join([self.template_path, 'properties.sql']))
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did):
|
||||
"""
|
||||
Lists all extensions under the Extensions Collection node
|
||||
"""
|
||||
res = []
|
||||
SQL = render_template("/".join([self.template_path, 'properties.sql']))
|
||||
status, rset = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['eid'],
|
||||
did,
|
||||
row['name'],
|
||||
'icon-extension'
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, eid):
|
||||
"""
|
||||
Fetch the properties of a single extension and render in properties tab
|
||||
"""
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'properties.sql']), eid=eid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did):
|
||||
"""
|
||||
Create a new extension object
|
||||
"""
|
||||
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
|
||||
)
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_dict(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'create.sql']),
|
||||
data=data
|
||||
)
|
||||
)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
status, rset = self.conn.execute_dict(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
ename=data['name']
|
||||
)
|
||||
)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['eid'],
|
||||
did,
|
||||
row['name'],
|
||||
'icon-extension'
|
||||
)
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function will update an extension object
|
||||
"""
|
||||
data = request.form if request.form else \
|
||||
json.loads(request.data.decode())
|
||||
SQL = self.getSQL(gid, sid, data, did, eid)
|
||||
|
||||
try:
|
||||
if SQL and isinstance(SQL, basestring) and \
|
||||
SQL.strip('\n') and SQL.strip(' '):
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Extension updated",
|
||||
data={
|
||||
'id': eid,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function will drop/drop cascade a extension object
|
||||
"""
|
||||
cascade = True if self.cmd == 'delete' else False
|
||||
try:
|
||||
# check if extension with eid exists
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']), eid=eid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
# drop extension
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']
|
||||
), name=name, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Extension dropped"),
|
||||
data={
|
||||
'id': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, eid=None):
|
||||
"""
|
||||
This function returns modified SQL
|
||||
"""
|
||||
data = request.args.copy()
|
||||
SQL = self.getSQL(gid, sid, data, did, eid)
|
||||
if SQL and isinstance(SQL, basestring) and SQL.strip('\n') \
|
||||
and SQL.strip(' '):
|
||||
return make_json_response(
|
||||
data=SQL,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data=gettext('-- Modified SQL --'),
|
||||
status=200
|
||||
)
|
||||
|
||||
def getSQL(self, gid, sid, data, did, eid=None):
|
||||
"""
|
||||
This function will generate sql from model data
|
||||
"""
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if eid is not None:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'properties.sql']
|
||||
), eid=eid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'update.sql']
|
||||
), data=data, o_data=old_data)
|
||||
else:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'create.sql']
|
||||
), data=data)
|
||||
return SQL
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def avails(self, gid, sid, did):
|
||||
"""
|
||||
This function with fetch all the available extensions
|
||||
"""
|
||||
SQL = render_template("/".join([self.template_path, 'extensions.sql']))
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
return make_json_response(
|
||||
data=rset['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def schemas(self, gid, sid, did):
|
||||
"""
|
||||
This function with fetch all the schemas
|
||||
"""
|
||||
SQL = render_template("/".join([self.template_path, 'schemas.sql']))
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
return make_json_response(
|
||||
data=rset['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
def module_js(self):
|
||||
"""
|
||||
This property defines whether javascript exists for this node.
|
||||
"""
|
||||
return make_response(
|
||||
render_template(
|
||||
"extensions/js/extensions.js",
|
||||
_=gettext
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'}
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function will generate sql for the sql panel
|
||||
"""
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'properties.sql']
|
||||
), eid=eid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result = res['rows'][0]
|
||||
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'create.sql']
|
||||
),
|
||||
data=result,
|
||||
conn=self.conn,
|
||||
display_comments=True
|
||||
)
|
||||
|
||||
return ajax_response(response=SQL)
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function gets the dependents and returns an ajax response
|
||||
for the extension node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
eid: Extension ID
|
||||
"""
|
||||
dependents_result = self.get_dependents(self.conn, eid)
|
||||
return ajax_response(
|
||||
response=dependents_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def dependencies(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function gets the dependencies and returns an ajax response
|
||||
for the extension node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
lid: Extension ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(self.conn, eid)
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
# Register and add ExtensionView as blueprint
|
||||
ExtensionView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 1017 B |
Binary file not shown.
After Width: | Height: | Size: 996 B |
|
@ -0,0 +1,254 @@
|
|||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin',
|
||||
'pgadmin.browser', 'pgadmin.browser.collection'],
|
||||
function($, _, S, pgAdmin, pgBrowser) {
|
||||
|
||||
/*
|
||||
* Create and Add an Extension Collection into nodes
|
||||
* Params:
|
||||
* label - Label for Node
|
||||
* type - Type of Node
|
||||
* columns - List of columns to show under under properties.
|
||||
*/
|
||||
if (!pgBrowser.Nodes['coll-extension']) {
|
||||
var extensions = pgAdmin.Browser.Nodes['coll-extension'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'extension',
|
||||
label: '{{ _('Extension') }}',
|
||||
type: 'coll-extension',
|
||||
columns: ['name', 'owner', 'comment']
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Create and Add an Extension Node into nodes
|
||||
* Params:
|
||||
* parent_type - Name of parent Node
|
||||
* type - Type of Node
|
||||
* hasSQL - True if we need to show SQL query Tab control, otherwise False
|
||||
* canDrop - True to show "Drop Extension" link under Context menu,
|
||||
* otherwise False
|
||||
* canDropCascade - True to show "Drop Cascade" link under Context menu,
|
||||
* otherwise False
|
||||
* columns - List of columns to show under under properties tab.
|
||||
* label - Label for Node
|
||||
*/
|
||||
if (!pgBrowser.Nodes['extension']) {
|
||||
pgAdmin.Browser.Nodes['extension'] =
|
||||
pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'database',
|
||||
type: 'extension',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
label: '{{ _('Extension') }}',
|
||||
|
||||
Init: function() {
|
||||
if(this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
/*
|
||||
* Add "create extension" menu item into context and object menu
|
||||
* for the following nodes:
|
||||
* coll-extension, extension and database.
|
||||
*/
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_extension_on_coll', node: 'coll-extension', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Extension...') }}',
|
||||
icon: 'wcTabIcon icon-extension', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_extension', node: 'extension', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Extension...') }}',
|
||||
icon: 'wcTabIcon icon-extension', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_extension', node: 'database', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Extension...') }}',
|
||||
icon: 'wcTabIcon icon-extension', data: {action: 'create'}
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
/*
|
||||
* Define model for the Node and specify the properties
|
||||
* of the model in schema.
|
||||
*/
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
schema: [
|
||||
{
|
||||
id: 'name', label: '{{ _('Name')}}', first_empty: true,
|
||||
type: 'text', mode: ['properties', 'create', 'edit'],
|
||||
visible: true, url:'avails', disabled: function(m) {
|
||||
return !m.isNew();
|
||||
},
|
||||
transform: function(data) {
|
||||
var res = [];
|
||||
var label = this.model.get('name');
|
||||
if (!this.model.isNew()) {
|
||||
res.push({label: label, value: label});
|
||||
}
|
||||
else {
|
||||
if (data && _.isArray(data)) {
|
||||
_.each(data, function(d) {
|
||||
if (d.installed_version === null)
|
||||
|
||||
/*
|
||||
* d contains json data and sets into
|
||||
* select's option control
|
||||
*
|
||||
* We need to stringify data because formatter will
|
||||
* convert Array Object as [Object] string
|
||||
*/
|
||||
res.push({label: d.name, value: JSON.stringify(d)});
|
||||
})
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
/*
|
||||
* extends NodeAjaxOptionsControl to override the properties
|
||||
* getValueFromDOM which takes stringified data from option of
|
||||
* select control and parse it. And `onChange` takes the stringified
|
||||
* data from select's option, thus convert it to json format and set the
|
||||
* data into Model which is used to enable/disable the schema field.
|
||||
*/
|
||||
control: Backform.NodeAjaxOptionsControl.extend({
|
||||
getValueFromDOM: function() {
|
||||
var data = this.formatter.toRaw(
|
||||
_.unescape(this.$el.find("select").val()), this.model);
|
||||
/*
|
||||
* return null if data is empty to prevent it from
|
||||
* throwing parsing error. Adds check as name can be empty
|
||||
*/
|
||||
if (data === '') {
|
||||
return null;
|
||||
}
|
||||
else if (typeof(data) === 'string') {
|
||||
data=JSON.parse(data);
|
||||
}
|
||||
return data.name;
|
||||
},
|
||||
|
||||
/*
|
||||
* When name is changed, extract value from its select option and
|
||||
* set attributes values into the model
|
||||
*/
|
||||
onChange: function() {
|
||||
Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
|
||||
var selectedValue = this.$el.find("select").val();
|
||||
if (selectedValue.trim() != "") {
|
||||
var d = this.formatter.toRaw(selectedValue, this.model);
|
||||
if(typeof(d) === 'string')
|
||||
d=JSON.parse(d);
|
||||
var changes = {
|
||||
'version' : '',
|
||||
'relocatable': (
|
||||
(!_.isNull(d.relocatable[0]) && !_.isUndefined(d.relocatable[0])) ?
|
||||
d.relocatable[0]: ''),
|
||||
'schema': ((!_.isNull(d.schema[0]) &&
|
||||
!_.isUndefined(d.schema[0])) ? d.schema[0]: '')
|
||||
};
|
||||
this.model.set(changes);
|
||||
}
|
||||
else {
|
||||
var changes = {'version': '', 'relocatable': true, 'schema': ''};
|
||||
this.model.set(changes);
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
id: 'eid', label: '{{ _('Oid')}}', cell: 'string',
|
||||
type: 'text', disabled: true, mode: ['properties', 'edit', 'create']
|
||||
},
|
||||
{
|
||||
id: 'owner', label:'{{ _('Owner') }}', control: 'node-list-by-name',
|
||||
mode: ['properties'], node: 'role', cell: 'string'
|
||||
},
|
||||
{
|
||||
id: 'schema', label: '{{ _('Schema')}}', type: 'text', control: 'node-ajax-options',
|
||||
mode: ['properties', 'create', 'edit'], group: 'Definition', deps: ['relocatable'],
|
||||
url: 'schemas', first_empty: true, disabled: function(m) {
|
||||
|
||||
/*
|
||||
* enable or disable schema field if model's relocatable
|
||||
* attribute is True or False
|
||||
*/
|
||||
return (m.has('relocatable') ? !m.get('relocatable') : false);
|
||||
},
|
||||
transform: function(data) {
|
||||
var res = [];
|
||||
if (data && _.isArray(data)) {
|
||||
_.each(data, function(d) {
|
||||
res.push({label: d.schema, value: d.schema});
|
||||
})
|
||||
}
|
||||
return res;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'relocatable', label: '{{ _('Relocatable?')}}', cell: 'switch',
|
||||
type: 'switch', mode: ['properties'], 'options': {
|
||||
'onText': 'Yes', 'offText': 'No', 'onColor': 'success',
|
||||
'offColor': 'default', 'size': 'small'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'version', label: '{{ _('Version')}}', cell: 'string',
|
||||
mode: ['properties', 'create', 'edit'], group: 'Definition',
|
||||
control: 'node-ajax-options', url:'avails', first_empty: true,
|
||||
|
||||
// Transform the data into version for the selected extension.
|
||||
transform: function(data) {
|
||||
res = [];
|
||||
var extension = this.model.get('name');
|
||||
_.each(data, function(dt) {
|
||||
if(dt.name == extension) {
|
||||
if(dt.version && _.isArray(dt.version)) {
|
||||
_.each(dt.version, function(v) {
|
||||
res.push({ label: v, value: v });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'comment', label: '{{ _('Comment')}}', cell: 'string',
|
||||
type: 'multiline', disabled: true
|
||||
}
|
||||
],
|
||||
validate: function() {
|
||||
|
||||
/*
|
||||
* Triggers error messages for name
|
||||
* if it is empty/undefined/null
|
||||
*/
|
||||
var err = {},
|
||||
errmsg,
|
||||
name = this.get('name');
|
||||
if (_.isUndefined(name) || _.isNull(name) ||
|
||||
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['name'] = '{{ _('Name can not be empty!') }}';
|
||||
errmsg = errmsg || err['name'];
|
||||
this.errorModel.set('name', errmsg);
|
||||
return errmsg;
|
||||
}
|
||||
else {
|
||||
this.errorModel.unset('name');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
return pgBrowser.Nodes['coll-extension'];
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
{#=========================Create new extension======================#}
|
||||
{#===Generates comments and code for SQL tab===#}
|
||||
{% if display_comments %}
|
||||
-- Extension: {{ conn|qtIdent(data.name) }}
|
||||
|
||||
-- DROP EXTENSION {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.name %}
|
||||
CREATE EXTENSION {{ conn|qtIdent(data.name) }}{% if data.schema == '' and data.version == '' %};{% endif %}
|
||||
{% if data.schema %}
|
||||
|
||||
SCHEMA {{ conn|qtIdent(data.schema) }}{% if data.version == '' %};{% endif %}
|
||||
{% endif %}
|
||||
{% if data.version %}
|
||||
|
||||
VERSION {{ conn|qtIdent(data.version) }};
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,8 @@
|
|||
{#============================Drop/Cascade Extension by name=========================#}
|
||||
{% if eid %}
|
||||
SELECT x.extname from pg_extension x
|
||||
WHERE x.oid = {{ eid }}::int
|
||||
{% endif %}
|
||||
{% if name %}
|
||||
DROP EXTENSION {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1,12 @@
|
|||
{# ======================Fetch extensions names=====================#}
|
||||
SELECT
|
||||
a.name, a.installed_version,
|
||||
array_agg(av.version) as version,
|
||||
array_agg(av.schema) as schema,
|
||||
array_agg(av.superuser) as superuser,
|
||||
array_agg(av.relocatable) as relocatable
|
||||
FROM
|
||||
pg_available_extensions a
|
||||
LEFT JOIN pg_available_extension_versions av ON (a.name = av.name)
|
||||
GROUP BY a.name, a.installed_version
|
||||
ORDER BY a.name
|
|
@ -0,0 +1,17 @@
|
|||
{#===================Fetch properties of each extension by name or oid===================#}
|
||||
SELECT
|
||||
x.oid AS eid, pg_get_userbyid(extowner) AS owner,
|
||||
x.extname AS name, n.nspname AS schema,
|
||||
x.extrelocatable AS relocatable, x.extversion AS version,
|
||||
e.comment
|
||||
FROM
|
||||
pg_extension x
|
||||
LEFT JOIN pg_namespace n ON x.extnamespace=n.oid
|
||||
JOIN pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name
|
||||
{%- if eid %}
|
||||
WHERE x.oid = {{eid}}::int
|
||||
{% elif ename %}
|
||||
WHERE x.extname = {{ename|qtLiteral}}::text
|
||||
{% else %}
|
||||
ORDER BY x.extname
|
||||
{% endif %}
|
|
@ -0,0 +1,3 @@
|
|||
{#===================fetch all schemas==========================#}
|
||||
SELECT nspname As schema FROM pg_namespace
|
||||
ORDER BY nspname
|
|
@ -0,0 +1,10 @@
|
|||
{# =============Update extension schema============= #}
|
||||
{% if data.schema and data.schema != o_data.schema %}
|
||||
ALTER EXTENSION {{ conn|qtIdent(o_data.name) }}
|
||||
SET SCHEMA {{ conn|qtIdent(data.schema) }};
|
||||
{% endif %}
|
||||
{# =============Update extension version============= #}
|
||||
{% if data.version and data.version != o_data.version %}
|
||||
ALTER EXTENSION {{ conn|qtIdent(o_data.name) }}
|
||||
UPDATE TO {{ conn|qtIdent(data.version) }};
|
||||
{% endif %}
|
Loading…
Reference in New Issue