2015-02-15 16:10:53 -06:00
|
|
|
##########################################################################
|
|
|
|
#
|
|
|
|
# pgAdmin 4 - PostgreSQL Tools
|
|
|
|
#
|
2015-02-25 14:25:41 -06:00
|
|
|
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
|
2015-02-15 16:10:53 -06:00
|
|
|
# This software is released under the PostgreSQL Licence
|
|
|
|
#
|
|
|
|
##########################################################################
|
2015-06-29 01:58:41 -05:00
|
|
|
from abc import ABCMeta, abstractmethod, abstractproperty
|
|
|
|
from pgadmin import current_blueprint
|
|
|
|
from pgadmin.utils import PgAdminModule
|
|
|
|
from pgadmin.utils.ajax import make_json_response
|
|
|
|
from pgadmin.settings import get_setting
|
|
|
|
from flask import current_app, render_template, url_for, make_response
|
|
|
|
from flask.ext.security import login_required
|
|
|
|
from flask.ext.login import current_user
|
2015-10-20 02:03:18 -05:00
|
|
|
from flask.ext.babel import gettext
|
2015-06-29 01:58:41 -05:00
|
|
|
from flaskext.gravatar import Gravatar
|
2015-11-06 04:23:19 -06:00
|
|
|
import six
|
2015-02-15 16:10:53 -06:00
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
MODULE_NAME = 'browser'
|
|
|
|
|
2015-10-20 02:03:18 -05:00
|
|
|
class BrowserModule(PgAdminModule):
|
2015-06-29 01:58:41 -05:00
|
|
|
|
|
|
|
def get_own_stylesheets(self):
|
|
|
|
stylesheets = []
|
|
|
|
# Add browser stylesheets
|
|
|
|
for (endpoint, filename) in [
|
2015-10-20 02:03:18 -05:00
|
|
|
('static', 'css/codemirror/codemirror.css'),
|
|
|
|
('static', 'css/jQuery-contextMenu/jquery.contextMenu.css'),
|
|
|
|
('static', 'css/wcDocker/wcDocker.css' if current_app.debug
|
|
|
|
else 'css/wcDocker/wcDocker.min.css'),
|
|
|
|
('browser.static', 'css/browser.css'),
|
|
|
|
('browser.static', 'css/aciTree/css/aciTree.css')
|
|
|
|
]:
|
2015-06-29 01:58:41 -05:00
|
|
|
stylesheets.append(url_for(endpoint, filename=filename))
|
|
|
|
stylesheets.append(url_for('browser.browser_css'))
|
|
|
|
return stylesheets
|
|
|
|
|
|
|
|
def get_own_javascripts(self):
|
2015-06-30 00:51:55 -05:00
|
|
|
scripts = list()
|
|
|
|
scripts.append({
|
|
|
|
'name': 'alertify',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'static',
|
|
|
|
filename='js/alertifyjs/alertify' if current_app.debug
|
|
|
|
else 'js/alertifyjs/alertify.min'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'exports': 'alertify',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'codemirror',
|
|
|
|
'path': url_for('static', filename='js/codemirror/codemirror'),
|
|
|
|
'exports': 'CodeMirror',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'codemirror.sql',
|
|
|
|
'path': url_for('static', filename='js/codemirror/mode/sql'),
|
|
|
|
'deps': ['codemirror'],
|
|
|
|
'exports': 'CodeMirror.modes.sql',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'jqueryui.position',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'static',
|
|
|
|
filename='js/jQuery-contextMenu/jquery.ui.position'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'deps': ['jquery'],
|
|
|
|
'exports': 'jQuery.ui.position',
|
|
|
|
'preloaded': True
|
|
|
|
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'jquery.contextmenu',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'static',
|
|
|
|
filename='js/jQuery-contextMenu/jquery.contextMenu'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'deps': ['jquery', 'jqueryui.position'],
|
|
|
|
'exports': 'jQuery.contextMenu',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'jquery.aciplugin',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'browser.static',
|
|
|
|
filename='js/aciTree/jquery.aciPlugin.min'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'deps': ['jquery'],
|
|
|
|
'exports': 'aciPluginClass',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'jquery.acitree',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'browser.static',
|
|
|
|
filename='js/aciTree/jquery.aciTree' if
|
|
|
|
current_app.debug else 'js/aciTree/jquery.aciTree.min'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'deps': ['jquery', 'jquery.aciplugin'],
|
|
|
|
'exports': 'aciPluginClass.plugins.aciTree',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
scripts.append({
|
|
|
|
'name': 'wcdocker',
|
2015-10-20 02:03:18 -05:00
|
|
|
'path': url_for(
|
|
|
|
'static',
|
|
|
|
filename='js/wcDocker/wcDocker' if current_app.debug
|
|
|
|
else 'js/wcDocker/wcDocker.min'
|
|
|
|
),
|
2015-06-30 00:51:55 -05:00
|
|
|
'deps': ['jquery.contextmenu'],
|
|
|
|
'exports': '',
|
|
|
|
'preloaded': True
|
|
|
|
})
|
|
|
|
|
|
|
|
for name, script in [
|
|
|
|
['pgadmin.browser', 'js/browser'],
|
|
|
|
['pgadmin.browser.error', 'js/error'],
|
2015-11-17 00:21:09 -06:00
|
|
|
['pgadmin.browser.node', 'js/node'],
|
|
|
|
['pgadmin.browser.collection', 'js/collection']]:
|
2015-10-20 02:03:18 -05:00
|
|
|
scripts.append({
|
|
|
|
'name': name,
|
|
|
|
'path': url_for('browser.index') + script,
|
|
|
|
'preloaded': True
|
|
|
|
})
|
2015-06-30 00:51:55 -05:00
|
|
|
|
|
|
|
for name, end in [
|
|
|
|
['pgadmin.browser.menu', 'js/menu'],
|
|
|
|
['pgadmin.browser.panel', 'js/panel'],
|
|
|
|
['pgadmin.browser.frame', 'js/frame']]:
|
|
|
|
scripts.append({
|
|
|
|
'name': name, 'path': url_for('browser.static', filename=end),
|
|
|
|
'preloaded': True})
|
|
|
|
|
2015-12-27 13:30:20 -06:00
|
|
|
scripts.append({
|
|
|
|
'name': 'pgadmin.browser.node.ui',
|
|
|
|
'path': url_for('browser.static', filename='js/node.ui'),
|
|
|
|
'when': 'server-group'
|
|
|
|
})
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
for module in self.submodules:
|
|
|
|
scripts.extend(module.get_own_javascripts())
|
2015-06-29 01:58:41 -05:00
|
|
|
return scripts
|
|
|
|
|
|
|
|
|
|
|
|
blueprint = BrowserModule(MODULE_NAME, __name__)
|
|
|
|
|
2015-11-06 04:23:19 -06:00
|
|
|
@six.add_metaclass(ABCMeta)
|
2015-06-29 01:58:41 -05:00
|
|
|
class BrowserPluginModule(PgAdminModule):
|
|
|
|
"""
|
|
|
|
Base class for browser submodules.
|
|
|
|
"""
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
browser_url_prefix = blueprint.url_prefix + '/'
|
2015-06-29 01:58:41 -05:00
|
|
|
|
|
|
|
def __init__(self, import_name, **kwargs):
|
|
|
|
kwargs.setdefault("url_prefix", self.node_path)
|
2015-06-30 00:51:55 -05:00
|
|
|
kwargs.setdefault("static_url_path", '/static')
|
2015-10-20 02:03:18 -05:00
|
|
|
super(BrowserPluginModule, self).__init__(
|
|
|
|
"NODE-%s" % self.node_type,
|
2015-06-29 01:58:41 -05:00
|
|
|
import_name,
|
2015-10-20 02:03:18 -05:00
|
|
|
**kwargs
|
|
|
|
)
|
2015-06-29 01:58:41 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def jssnippets(self):
|
|
|
|
"""
|
|
|
|
Returns a snippet of javascript to include in the page
|
|
|
|
"""
|
|
|
|
return []
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
def get_own_javascripts(self):
|
|
|
|
scripts = []
|
|
|
|
|
|
|
|
scripts.extend([{
|
|
|
|
'name': 'pgadmin.node.%s' % self.node_type,
|
|
|
|
'path': url_for('browser.index') + '%s/module' % self.node_type,
|
|
|
|
'when': self.script_load
|
|
|
|
}])
|
|
|
|
|
|
|
|
for module in self.submodules:
|
|
|
|
scripts.extend(module.get_own_javascripts())
|
|
|
|
return scripts
|
|
|
|
|
2015-10-20 02:03:18 -05:00
|
|
|
def generate_browser_node(
|
2015-11-19 11:45:48 -06:00
|
|
|
self, node_id, label, icon, inode, node_type, **kwargs
|
2015-10-20 02:03:18 -05:00
|
|
|
):
|
2015-06-30 00:51:55 -05:00
|
|
|
obj = {
|
2015-10-20 02:03:18 -05:00
|
|
|
"id": "%s/%s" % (node_type, node_id),
|
|
|
|
"label": label,
|
|
|
|
"icon": icon,
|
|
|
|
"inode": inode,
|
|
|
|
"_type": node_type,
|
|
|
|
"_id": node_id,
|
|
|
|
"module": 'pgadmin.node.%s' % node_type
|
|
|
|
}
|
|
|
|
for key in kwargs:
|
|
|
|
obj.setdefault(key, kwargs[key])
|
2015-06-30 00:51:55 -05:00
|
|
|
return obj
|
|
|
|
|
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
@property
|
|
|
|
def csssnippets(self):
|
|
|
|
"""
|
|
|
|
Returns a snippet of css to include in the page
|
|
|
|
"""
|
2015-10-20 02:03:18 -05:00
|
|
|
snippets = [
|
|
|
|
render_template(
|
|
|
|
"browser/css/node.css",
|
|
|
|
node_type=self.node_type,
|
|
|
|
_=gettext
|
|
|
|
)]
|
2015-06-29 01:58:41 -05:00
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
for submodule in self.submodules:
|
|
|
|
snippets.extend(submodule.csssnippets)
|
|
|
|
return snippets
|
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
@abstractmethod
|
|
|
|
def get_nodes(self):
|
|
|
|
"""
|
|
|
|
Each browser module is responsible for fetching
|
|
|
|
its own tree subnodes.
|
|
|
|
"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
@abstractproperty
|
|
|
|
def node_type(self):
|
|
|
|
pass
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
@abstractproperty
|
|
|
|
def script_load(self):
|
|
|
|
"""
|
|
|
|
This property defines, when to load this script.
|
|
|
|
In order to allow creation of an object, we need to load script for any
|
|
|
|
node at the parent level.
|
|
|
|
|
|
|
|
i.e.
|
|
|
|
- In order to allow creating a server object, it should be loaded at
|
|
|
|
server-group node.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
@property
|
|
|
|
def node_path(self):
|
2015-10-20 02:03:18 -05:00
|
|
|
return self.browser_url_prefix + self.node_type
|
2015-06-30 00:51:55 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def javascripts(self):
|
|
|
|
return []
|
2015-06-29 01:58:41 -05:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/")
|
|
|
|
@login_required
|
|
|
|
def index():
|
|
|
|
"""Render and process the main browser window."""
|
|
|
|
# Get the Gravatar
|
|
|
|
gravatar = Gravatar(current_app,
|
|
|
|
size=100,
|
|
|
|
rating='g',
|
|
|
|
default='retro',
|
|
|
|
force_default=False,
|
|
|
|
use_ssl=False,
|
|
|
|
base_url=None)
|
2015-10-20 02:03:18 -05:00
|
|
|
return render_template(
|
|
|
|
MODULE_NAME + "/index.html",
|
|
|
|
username=current_user.email,
|
|
|
|
_=gettext
|
|
|
|
)
|
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
@blueprint.route("/js/browser.js")
|
2015-06-29 01:58:41 -05:00
|
|
|
@login_required
|
|
|
|
def browser_js():
|
|
|
|
layout = get_setting('Browser/Layout', default='')
|
|
|
|
snippets = []
|
|
|
|
for submodule in current_blueprint.submodules:
|
|
|
|
snippets.extend(submodule.jssnippets)
|
2015-06-29 03:11:56 -05:00
|
|
|
return make_response(
|
|
|
|
render_template(
|
|
|
|
'browser/js/browser.js',
|
|
|
|
layout=layout,
|
2015-10-20 02:03:18 -05:00
|
|
|
jssnippets=snippets,
|
|
|
|
_=gettext
|
|
|
|
),
|
2015-06-29 03:11:56 -05:00
|
|
|
200, {'Content-Type': 'application/x-javascript'})
|
2015-06-29 01:58:41 -05:00
|
|
|
|
2015-10-20 02:03:18 -05:00
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
@blueprint.route("/js/error.js")
|
|
|
|
@login_required
|
|
|
|
def error_js():
|
|
|
|
return make_response(
|
2015-10-20 02:03:18 -05:00
|
|
|
render_template('browser/js/error.js', _=gettext),
|
2015-06-30 00:51:55 -05:00
|
|
|
200, {'Content-Type': 'application/x-javascript'})
|
|
|
|
|
2015-10-20 02:03:18 -05:00
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
@blueprint.route("/js/node.js")
|
|
|
|
@login_required
|
|
|
|
def node_js():
|
|
|
|
return make_response(
|
2015-10-20 02:03:18 -05:00
|
|
|
render_template('browser/js/node.js', _=gettext),
|
2015-06-30 00:51:55 -05:00
|
|
|
200, {'Content-Type': 'application/x-javascript'})
|
|
|
|
|
2015-11-17 00:21:09 -06:00
|
|
|
@blueprint.route("/js/collection.js")
|
|
|
|
@login_required
|
|
|
|
def collection_js():
|
|
|
|
return make_response(
|
|
|
|
render_template('browser/js/collection.js', _=gettext),
|
|
|
|
200, {'Content-Type': 'application/x-javascript'})
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
|
2015-06-29 01:58:41 -05:00
|
|
|
@blueprint.route("/browser.css")
|
|
|
|
@login_required
|
|
|
|
def browser_css():
|
|
|
|
"""Render and return CSS snippets from the nodes and modules."""
|
|
|
|
snippets = []
|
2015-06-30 00:51:55 -05:00
|
|
|
for submodule in blueprint.submodules:
|
2015-06-29 01:58:41 -05:00
|
|
|
snippets.extend(submodule.csssnippets)
|
2015-06-29 03:11:56 -05:00
|
|
|
return make_response(
|
2015-10-20 02:03:18 -05:00
|
|
|
render_template(
|
|
|
|
'browser/css/browser.css', snippets=snippets, _=gettext
|
|
|
|
),
|
2015-06-29 03:11:56 -05:00
|
|
|
200, {'Content-Type': 'text/css'})
|
2015-06-29 01:58:41 -05:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route("/nodes/")
|
|
|
|
@login_required
|
|
|
|
def get_nodes():
|
|
|
|
"""Build a list of treeview nodes from the child nodes."""
|
|
|
|
nodes = []
|
|
|
|
for submodule in current_blueprint.submodules:
|
|
|
|
nodes.extend(submodule.get_nodes())
|
|
|
|
return make_json_response(data=nodes)
|