Monitor connection and transaction status in the query tool. Fixes #2475

pull/18/head
Murtuza Zabuawala 2018-01-12 14:34:39 +00:00 committed by Dave Page
parent 82aa8035c2
commit 8520871bc6
12 changed files with 470 additions and 52 deletions

View File

@ -886,9 +886,12 @@ class ServerNode(PGChildNodeView):
if server is None:
return bad_request(gettext("Server not found."))
# Fetch User Details.
user = User.query.filter_by(id=current_user.id).first()
if user is None:
if current_user and hasattr(current_user, 'id'):
# Fetch User Details.
user = User.query.filter_by(id=current_user.id).first()
if user is None:
return unauthorized(gettext("Unauthorized request."))
else:
return unauthorized(gettext("Unauthorized request."))
data = request.form if request.form else json.loads(

View File

@ -9,6 +9,6 @@
.icon-database-not-connected {
background-image: url('{{ url_for('NODE-database.static', filename='img/databasebad.svg') }}') !important;
border-radius: 10px
border-radius: 10px;
background-size: 20px !important;
}

View File

@ -1438,3 +1438,9 @@ body {
.multi-checkbox .check.partial:after {
content: "\003F";
}
/* Override default bootstrap popover fonts & size */
.popover-content {
font-family: 'Open Sans';
font-size: 13px;
}

View File

@ -8,8 +8,8 @@
//////////////////////////////////////////////////////////////////////////
// This file contains common utilities functions used in sqleditor modules
define(['jquery'],
function ($) {
define(['jquery', 'sources/gettext'],
function ($, gettext) {
var sqlEditorUtils = {
/* Reference link http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
* Modified as per requirement.
@ -56,6 +56,143 @@ define(['jquery'],
capitalizeFirstLetter: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
// Status flag
previousStatus: null,
// This function will fetch the connection status via ajax
fetchConnectionStatus: function(url, $el, $status_el) {
// If user has switch the browser Tab or Minimized window or
// if wcDocker panel is not in focus then don't fire AJAX
if (document.visibilityState !== 'visible' ||
$el.data('panel-visible') !== 'visible' ) {
return;
}
// Start polling..
$.ajax({
url: url,
method: 'GET',
success: function (res) {
if(res && res.data) {
var status = res.data.status,
msg = res.data.message,
is_status_changed = false;
// Inject CSS as required
switch(status) {
// Busy
case 1:
// if received busy status more than once then only
if(status == sqlEditorUtils.previousStatus &&
!$status_el.hasClass('fa-hourglass-half')) {
$status_el.removeClass()
.addClass('fa fa-hourglass-half');
is_status_changed = true;
}
break;
// Idle in transaction
case 2:
if(sqlEditorUtils.previousStatus != status &&
!$status_el.hasClass('fa-clock-o')) {
$status_el.removeClass()
.addClass('fa fa-clock-o');
is_status_changed = true;
}
break;
// Failed in transaction
case 3:
if(sqlEditorUtils.previousStatus != status &&
!$status_el.hasClass('fa-exclamation-circle')) {
$status_el.removeClass()
.addClass('fa fa-exclamation-circle');
is_status_changed = true;
}
break;
// Failed in transaction with unknown server side error
case 4:
if(sqlEditorUtils.previousStatus != status &&
!$status_el.hasClass('fa-exclamation-triangle')) {
$status_el.removeClass()
.addClass('fa fa-exclamation-triangle');
is_status_changed = true;
}
break;
default:
if(sqlEditorUtils.previousStatus != status &&
!$status_el.hasClass('fa-query_tool_connected')) {
$status_el.removeClass()
.addClass('fa-custom fa-query-tool-connected');
is_status_changed = true;
}
}
sqlEditorUtils.previousStatus = status;
// Set bootstrap popover message
if(is_status_changed) {
$el.popover('hide');
$el.attr('data-content', msg);
}
} else {
// We come here means we did not receive expected response
// from server, we need to error out
sqlEditorUtils.previousStatus = -99;
msg = gettext("An unexpected error occurred - " +
"ensure sure you are logged into the application.");
$el.attr('data-content', msg);
if(!$status_el.hasClass('fa-query-tool-disconnected')) {
$el.popover('hide');
$status_el.removeClass()
.addClass('fa-custom fa-query-tool-disconnected');
}
}
},
error: function (e) {
sqlEditorUtils.previousStatus = -1;
var msg = gettext("Transaction status check failed.");
if (e.readyState == 0) {
msg = gettext("Not connected to the server or the connection to " +
"the server has been closed.");
} else if (e.responseJSON && e.responseJSON.errormsg) {
msg = e.responseJSON.errormsg;
}
// Set bootstrap popover
$el.attr('data-content', msg);
// Add error class
if(!$status_el.hasClass('fa-query-tool-disconnected')) {
$el.popover('hide');
$status_el.removeClass()
.addClass('fa-custom fa-query-tool-disconnected');
}
}
});
},
// This function will update the connection status
updateConnectionStatus: function(url, poll_time) {
var $el = $(".connection_status"),
$status_el = $($($el).find(".fa-custom"));
// Apply popover on element
$el.popover();
// To set initial connection status
sqlEditorUtils.fetchConnectionStatus(url, $el, $status_el);
// Calling it again in specified interval
setInterval(
sqlEditorUtils.fetchConnectionStatus.bind(null, url, $el, $status_el),
poll_time * 1000
);
},
// Updates the flag for connection status poll
updateConnectionStatusFlag: function(status) {
var $el = $(".connection_status");
if ($el.data('panel-visible') != status) {
$el.data('panel-visible', status);
}
},
};
return sqlEditorUtils;
});

View File

@ -149,10 +149,11 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id):
else:
sql_grid_data = session['gridData']
# Use pickle to store the command object which will be used
# later by the sql grid module.
# Use pickle to store the command object which will be used later by the
# sql grid module.
sql_grid_data[trans_id] = {
'command_obj': pickle.dumps(command_obj, -1) # -1 specify the highest protocol version available
# -1 specify the highest protocol version available
'command_obj': pickle.dumps(command_obj, -1)
}
# Store the grid dictionary into the session variable
@ -195,15 +196,20 @@ def panel(trans_id, is_query_tool, editor_title):
user_agent = UserAgent(request.headers.get('User-Agent'))
"""
Animations and transitions are not automatically GPU accelerated and by default use browser's slow rendering engine.
We need to set 'translate3d' value of '-webkit-transform' property in order to use GPU.
After applying this property under linux, Webkit calculates wrong position of the elements so panel contents are not visible.
To make it work, we need to explicitly set '-webkit-transform' property to 'none' for .ajs-notifier, .ajs-message, .ajs-modal classes.
Animations and transitions are not automatically GPU accelerated and by
default use browser's slow rendering engine. We need to set 'translate3d'
value of '-webkit-transform' property in order to use GPU. After applying
this property under linux, Webkit calculates wrong position of the elements
so panel contents are not visible. To make it work, we need to explicitly
set '-webkit-transform' property to 'none' for .ajs-notifier,
.ajs-message, .ajs-modal classes.
This issue is only with linux runtime application and observed in Query tool and debugger.
When we open 'Open File' dialog then whole Query-tool panel content is not visible though it contains HTML element in back end.
This issue is only with linux runtime application and observed in Query
tool and debugger. When we open 'Open File' dialog then whole Query tool
panel content is not visible though it contains HTML element in back end.
The port number should have already been set by the runtime if we're running in desktop mode.
The port number should have already been set by the runtime if we're
running in desktop mode.
"""
is_linux_platform = False
@ -218,15 +224,18 @@ def panel(trans_id, is_query_tool, editor_title):
new_browser_tab = 'false'
if is_query_tool == 'true':
prompt_save_changes = pref.preference('prompt_save_query_changes').get()
prompt_save_changes = pref.preference(
'prompt_save_query_changes'
).get()
else:
prompt_save_changes = pref.preference('prompt_save_data_changes').get()
display_connection_status = pref.preference('connection_status').get()
# Fetch the server details
#
bgcolor = None
fgcolor = None
if str(trans_id) in session['gridData']:
if 'gridData' in session and str(trans_id) in session['gridData']:
# Fetch the object for the specified transaction id.
# Use pickle.loads function to get the command object
session_obj = session['gridData'][str(trans_id)]
@ -240,9 +249,12 @@ def panel(trans_id, is_query_tool, editor_title):
fgcolor = s.fgcolor or 'black'
return render_template(
"datagrid/index.html", _=gettext, uniqueId=trans_id,
"datagrid/index.html",
_=gettext,
uniqueId=trans_id,
is_query_tool=is_query_tool,
editor_title=editor_title, script_type_url=sURL,
editor_title=editor_title,
script_type_url=sURL,
is_desktop_mode=app.PGADMIN_RUNTIME,
is_linux=is_linux_platform,
is_new_browser_tab=new_browser_tab,
@ -252,7 +264,8 @@ def panel(trans_id, is_query_tool, editor_title):
fgcolor=fgcolor,
# convert python boolean value to equivalent js boolean literal before
# passing it to html template.
prompt_save_changes='true' if prompt_save_changes else 'false'
prompt_save_changes='true' if prompt_save_changes else 'false',
display_connection_status=display_connection_status
)

View File

@ -1,8 +1,5 @@
{% extends "base.html" %}
{% block title %}{{ config.APP_NAME }} - Datagrid{% endblock %}
{% block css_link %}
<link type="text/css" rel="stylesheet" href="{{ url_for('sqleditor.static', filename='css/sqleditor.css') }}">
{% endblock %}
{% block body %}
<style>
body {padding: 0px;}
@ -13,6 +10,14 @@
.alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
.sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
{% endif %}
{# Note: If we will display connection status then we have to provide some
space to display status icon else we can use all the space available #}
.editor-title {
width:{% if display_connection_status -%} calc(100% - 43px)
{% else %} 100% {%- endif %};
}
</style>
<div id="main-editor_panel">
<div id="fetching_data" class="wcLoadingIconContainer sql-editor-busy-fetching hide">
@ -284,7 +289,26 @@
</button>
</div>
</div>
<div class="editor-title" style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% else %}{{ bgcolor or '#2C76B4' }}{% endif %}; color: {{ fgcolor or 'white' }};"></div>
<div class="connection_status_wrapper">
{% if display_connection_status %}
<div style="display: inline-block;"
title="{{ _('Connection status (click for details) (<accesskey>+T)') }}">
<div class="connection_status" data-container="body"
data-toggle="popover" data-placement="bottom"
data-content=""
data-panel-visible="visible"
accesskey="t">
<i class="fa-custom fa-query-tool-disconnected" aria-hidden="true"
title="{{ _('Connection status (click for details) (<accesskey>+T)') }}">
</i>
</div>
</div>
{% endif %}
<div class="editor-title"
style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% else %}{{ bgcolor or '#2C76B4' }}{% endif %}; color: {{ fgcolor or 'white' }};"></div>
</div>
<div id="filter" class="filter-container hidden">
<div class="filter-title">Filter</div>
<div class="sql-textarea">
@ -368,7 +392,13 @@
{% endif %}
// Start the query tool.
sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}",
script_sql, {{ is_new_browser_tab }}, "{{ server_type }}", {{ prompt_save_changes }});
sqlEditorController.start(
{{ is_query_tool }},
"{{ editor_title }}",
script_sql,
{{ is_new_browser_tab }},
"{{ server_type }}",
{{ prompt_save_changes }}
);
});
{% endblock %}

View File

@ -21,7 +21,7 @@ from pgadmin.tools.sqleditor.command import QueryToolCommand
from pgadmin.utils import PgAdminModule
from pgadmin.utils import get_storage_directory
from pgadmin.utils.ajax import make_json_response, bad_request, \
success_return, internal_server_error
success_return, internal_server_error, unauthorized
from pgadmin.utils.driver import get_driver
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
from pgadmin.misc.file_manager import Filemanager
@ -50,6 +50,14 @@ TX_STATUS__ACTIVE = 1
TX_STATUS_INTRANS = 2
TX_STATUS_INERROR = 3
# Connection status codes mapping
CONNECTION_STATUS_MESSAGE_MAPPING = dict({
0: 'The session is idle and there is no current transaction.',
1: 'A command is currently in progress.',
2: 'The session is idle in a valid transaction block.',
3: 'The session is idle in a failed transaction block.',
4: 'The connection with the server is bad.'
})
class SqlEditorModule(PgAdminModule):
"""
@ -107,7 +115,8 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.autocomplete',
'sqleditor.load_file',
'sqleditor.save_file',
'sqleditor.query_tool_download'
'sqleditor.query_tool_download',
'sqleditor.connection_status'
]
def register_preferences(self):
@ -333,6 +342,24 @@ class SqlEditorModule(PgAdminModule):
}
)
self.display_connection_status = self.preference.register(
'display', 'connection_status',
gettext("Connection status"), 'boolean', True,
category_label=gettext('Display'),
help_str=gettext('If set to True, the Query Tool '
'will monitor and display the connection and '
'transaction status.')
)
self.connection_status = self.preference.register(
'display', 'connection_status_fetch_time',
gettext("Connection status refresh rate"), 'integer', 2,
min_val=1, max_val=600,
category_label=gettext('Display'),
help_str=gettext('The number of seconds between connection/transaction '
'status polls.')
)
blueprint = SqlEditorModule(MODULE_NAME, __name__, static_url_path='/static')
@ -345,10 +372,10 @@ def index():
def update_session_grid_transaction(trans_id, data):
grid_data = session['gridData']
grid_data[str(trans_id)] = data
session['gridData'] = grid_data
if 'gridData' in session:
grid_data = session['gridData']
grid_data[str(trans_id)] = data
session['gridData'] = grid_data
def check_transaction_status(trans_id):
@ -363,6 +390,10 @@ def check_transaction_status(trans_id):
Returns: status and connection object
"""
if 'gridData' not in session:
return False, unauthorized(gettext("Unauthorized request.")), \
None, None, None
grid_data = session['gridData']
# Return from the function if transaction id not found
@ -497,13 +528,23 @@ def start_query_tool(trans_id):
else:
sql = request.args or request.form
if 'gridData' not in session:
return make_json_response(
data={
'status': False,
'result': gettext('Transaction ID not found in the session.'),
'can_edit': False, 'can_filter': False
}
)
grid_data = session['gridData']
# Return from the function if transaction id not found
if str(trans_id) not in grid_data:
return make_json_response(
data={
'status': False, 'result': gettext('Transaction ID not found in the session.'),
'status': False,
'result': gettext('Transaction ID not found in the session.'),
'can_edit': False, 'can_filter': False
}
)
@ -1809,3 +1850,50 @@ def start_query_download_tool(trans_id):
return internal_server_error(
errormsg=gettext("Transaction status check failed.")
)
@blueprint.route(
'/status/<int:trans_id>',
methods=["GET"],
endpoint='connection_status'
)
@login_required
def query_tool_status(trans_id):
"""
The task of this function to return the status of the current connection
used in query tool instance with given transaction ID.
Args:
trans_id: Transaction ID
Returns:
Response with the connection status
Psycopg2 Status Code Mapping:
-----------------------------
TRANSACTION_STATUS_IDLE = 0
TRANSACTION_STATUS_ACTIVE = 1
TRANSACTION_STATUS_INTRANS = 2
TRANSACTION_STATUS_INERROR = 3
TRANSACTION_STATUS_UNKNOWN = 4
"""
status, error_msg, conn, trans_obj, \
session_obj = check_transaction_status(trans_id)
if not status and error_msg and type(error_msg) == str:
return internal_server_error(
errormsg=error_msg
)
if conn and trans_obj and session_obj:
status = conn.conn.get_transaction_status()
return make_json_response(
data={
'status': status,
'message': gettext(
CONNECTION_STATUS_MESSAGE_MAPPING.get(status)
)
}
)
else:
return internal_server_error(
errormsg=gettext("Transaction status check failed.")
)

View File

@ -14,7 +14,7 @@
.sql-editor-busy-fetching {
position:absolute;
left: 0;
top: 41px;
top: 65px;
bottom: 0;
right: 0;
margin:0;
@ -33,11 +33,6 @@
z-index: 0;
}
.editor-title {
padding: 4px 5px;
font-size: 13px;
}
.sql-editor-grid-container {
height: 100%;
overflow: auto;
@ -524,3 +519,56 @@ input.editor-checkbox:focus {
#datagrid .slick-row .slick-cell {
white-space: pre;
}
/* CSS for connection status icon */
#output-panel .CodeMirror {
border-top: 1px solid #ddd;
}
.connection_status_wrapper {
width: 100%;
background-color: #f7f7f7;
}
.editor-title {
padding: 4px 5px;
font-size: 13px;
display: inline-block;
}
.connection_status {
width: 11px;
position: relative;
display: inline-block;
margin-top: 2px;
margin-left: 12px;
margin-right: 16px;
}
.connection_status .fa {
font-size: 16px;
margin-left: 2px;
}
.connection_status .fa-clock-o,
.connection_status .fa-hourglass-half {
color: #e8a735;
}
.connection_status .fa-exclamation-circle,
.connection_status .fa-exclamation-triangle {
color: #d0021b;
}
.connection_status .fa-custom {
height: 18px;
margin-bottom: -4px;
}
.connection_status .fa-query-tool-connected {
content: url('../img/connect.svg');
}
.connection_status .fa-query-tool-disconnected {
content: url('../img/disconnect.svg');
}

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#2c76b4;}</style></defs><title>connect</title><path class="cls-1" d="M21.27,4.25c.12-.12.12-.18,0-.29-.32-.3-.63-.61-.92-.93-.12-.13-.19-.13-.31,0-.69.71-1.4,1.4-2.1,2.11L17.7,4.9a4.22,4.22,0,0,0-5.26-.82,9.75,9.75,0,0,0-1.69,1.46l-.58.55c-.18-.18-.36-.35-.53-.53s-.14-.1-.23,0c-.31.33-.63.65-1,1-.12.12-.13.18,0,.3.69.67,1.36,1.37,2.05,2,.15.14.13.21,0,.34l-2.05,2c-.12.12-.12.18,0,.29.32.3.63.61.92.93.12.13.19.13.31,0l2-2.05c.12-.13.19-.15.33,0,.61.63,1.24,1.25,1.87,1.87.13.12.12.19,0,.31l-2.07,2.06c-.11.11-.13.17,0,.29q.47.44.91.91c.13.14.2.16.35,0,.67-.69,1.36-1.36,2-2.05.12-.13.19-.12.31,0l2.08,2.09c.09.1.15.12.25,0,.32-.34.66-.67,1-1,.1-.1.07-.15,0-.23l-.61-.6c.48-.45,1-.86,1.41-1.32a4.13,4.13,0,0,0,1.2-3.67,4,4,0,0,0-1.42-2.55c-.14-.12-.14-.19,0-.32C20,5.56,20.61,4.9,21.27,4.25Zm-3.05,7.41c-.43.39-.81.83-1.2,1.23L11.37,7.25l1.42-1.34a2.52,2.52,0,0,1,3.48-.06C17,6.55,17.74,7.27,18.43,8A2.51,2.51,0,0,1,18.22,11.67Z"/><path class="cls-1" d="M15.52,17.16c-2-1.95-8-8-9-9C6.4,8,6.34,8,6.22,8.14c-.3.32-.62.64-.94.94-.11.11-.14.17,0,.28s.35.33.6.57c-.49.45-1,.88-1.42,1.34a4.16,4.16,0,0,0-1.12,4,4.37,4.37,0,0,0,1.33,2.22c.13.13.13.2,0,.33-.65.63-1.28,1.28-1.93,1.91-.13.13-.12.19,0,.31.32.3.62.61.92.92.11.12.18.12.29,0,.53-.55,1.07-1.08,1.62-1.62.15-.15.29-.42.47-.41s.31.25.47.39a4.21,4.21,0,0,0,5.81,0c.5-.47.94-1,1.43-1.53.25.26.43.44.6.63s.14.11.24,0c.3-.32.61-.63.93-.92C15.67,17.38,15.68,17.31,15.52,17.16Zm-3-.38c-.43.43-.84.87-1.27,1.29a2.53,2.53,0,0,1-3.74-.17c-.53-.57-1.1-1.11-1.66-1.64a2.58,2.58,0,0,1,0-3.93c.41-.41.79-.84,1.19-1.27l5.52,5.52C12.57,16.66,12.58,16.7,12.49,16.78Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#d0021b;}</style></defs><title>disconnect</title><path class="cls-1" d="M22.49,2.73c.12-.11.12-.18,0-.29-.31-.3-.62-.6-.91-.91-.12-.13-.18-.13-.31,0-.68.69-1.37,1.38-2.07,2.07L19,3.36a4.15,4.15,0,0,0-5.18-.81A9.59,9.59,0,0,0,12.14,4l-.57.54c-.18-.17-.36-.34-.53-.52s-.13-.09-.22,0c-.31.32-.62.64-.95.94-.12.11-.13.18,0,.3.68.66,1.34,1.34,2,2,.14.14.12.21,0,.34l-2,2c-.12.11-.12.18,0,.29.31.29.62.6.91.91.12.13.18.12.3,0l2-2c.12-.13.19-.15.32,0,.6.62,1.22,1.23,1.84,1.84.12.12.12.18,0,.3l-2,2c-.11.11-.13.17,0,.29q.46.43.89.89c.13.13.2.15.34,0,.66-.68,1.34-1.34,2-2,.12-.13.18-.12.3,0l2,2.05c.09.09.14.11.24,0,.32-.33.65-.66,1-1,.1-.09.07-.14,0-.22l-.6-.59c.47-.44.95-.85,1.38-1.3A4.06,4.06,0,0,0,22,7.47,4,4,0,0,0,20.56,5c-.14-.12-.14-.18,0-.31C21.2,4,21.84,3.36,22.49,2.73Zm-3,7.29c-.42.38-.8.81-1.18,1.21L12.75,5.68l1.39-1.32a2.48,2.48,0,0,1,3.42-.06C18.3,5,19,5.7,19.69,6.44A2.47,2.47,0,0,1,19.48,10Z"/><path class="cls-1" d="M14.1,18.75c-1.93-1.92-7.84-7.83-8.87-8.87-.11-.11-.16-.13-.28,0-.3.32-.61.63-.93.93-.11.1-.14.17,0,.28s.35.33.59.56c-.48.45-1,.86-1.4,1.32a4.09,4.09,0,0,0-1.1,3.93,4.3,4.3,0,0,0,1.31,2.19c.13.13.13.2,0,.33-.64.62-1.26,1.26-1.9,1.88-.13.12-.12.19,0,.3.31.29.61.6.91.91.11.12.17.12.29,0,.52-.54,1.06-1.07,1.59-1.59.15-.15.28-.41.46-.41s.31.24.46.38a4.14,4.14,0,0,0,5.72,0c.5-.46.92-1,1.4-1.51.25.26.42.43.59.62s.14.11.24,0c.3-.31.6-.62.91-.91C14.24,19,14.25,18.9,14.1,18.75Zm-3-.37c-.42.42-.82.86-1.25,1.27a2.49,2.49,0,0,1-3.68-.17c-.52-.56-1.08-1.09-1.64-1.62a2.54,2.54,0,0,1,0-3.87c.4-.4.78-.83,1.17-1.25l5.43,5.43C11.2,18.25,11.21,18.29,11.12,18.38Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -107,6 +107,8 @@ define('tools.querytool', [
filter = self.$el.find('#sql_filter');
$('.editor-title').text(_.unescape(self.editor_title));
self.checkConnectionStatus();
self.filter_obj = CodeMirror.fromTextArea(filter.get(0), {
lineNumbers: true,
mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql',
@ -114,8 +116,12 @@ define('tools.querytool', [
widget: '\u2026',
},
foldGutter: {
rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder),
rangeFinder: CodeMirror.fold.combine(
CodeMirror.pgadminBeginRangeFinder,
CodeMirror.pgadminIfRangeFinder,
CodeMirror.pgadminLoopRangeFinder,
CodeMirror.pgadminCaseRangeFinder
),
},
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: pgBrowser.editor_shortcut_keys,
@ -127,6 +133,20 @@ define('tools.querytool', [
matchBrackets: pgAdmin.Browser.editor_options.brace_matching,
});
// Updates connection status flag
self.gain_focus = function() {
setTimeout(function() {
SqlEditorUtils.updateConnectionStatusFlag('visible');
}, 100);
};
// Updates connection status flag
self.lost_focus = function() {
setTimeout(function() {
SqlEditorUtils.updateConnectionStatusFlag('hidden');
}, 100);
};
// Create main wcDocker instance
var main_docker = new wcDocker(
'#editor-panel', {
@ -136,7 +156,7 @@ define('tools.querytool', [
'filename': 'css',
}),
theme: 'webcabin.overrides.css',
});
});
var sql_panel = new pgAdmin.Browser.Panel({
name: 'sql_panel',
@ -162,8 +182,12 @@ define('tools.querytool', [
widget: '\u2026',
},
foldGutter: {
rangeFinder: CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder, CodeMirror.pgadminIfRangeFinder,
CodeMirror.pgadminLoopRangeFinder, CodeMirror.pgadminCaseRangeFinder),
rangeFinder: CodeMirror.fold.combine(
CodeMirror.pgadminBeginRangeFinder,
CodeMirror.pgadminIfRangeFinder,
CodeMirror.pgadminLoopRangeFinder,
CodeMirror.pgadminCaseRangeFinder
),
},
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: pgBrowser.editor_shortcut_keys,
@ -270,14 +294,47 @@ define('tools.querytool', [
// Set focus on query tool of active panel
p.on(wcDocker.EVENT.GAIN_FOCUS, function() {
if (!$(p.$container).hasClass('wcPanelTabContentHidden')) {
setTimeout(function() {
self.handler.gridView.query_tool_obj.focus();
}, 200);
var $container = $(this.$container),
transId = self.handler.transId;
// If transaction id is already set
if($container.data('trans-id') != transId) {
$container.data('trans-id', transId)
}
if (!$container.hasClass('wcPanelTabContentHidden')) {
setTimeout(function () {
self.handler.gridView.query_tool_obj.focus();
}, 200);
// Trigger an event to update connection status flag
pgBrowser.Events.trigger(
'pgadmin:query_tool:panel:gain_focus:' + transId
);
}
});
}
});
// When any query tool panel lost it focus then
p.on(wcDocker.EVENT.LOST_FOCUS, function () {
var $container = $(this.$container),
transId = $container.data('trans-id');
// Trigger an event to update connection status flag
if ($container.hasClass('wcPanelTabContentHidden')) {
pgBrowser.Events.trigger(
'pgadmin:query_tool:panel:lost_focus:' + transId
);
}
});
}
});
// Code to update connection status polling flag
pgBrowser.Events.on(
'pgadmin:query_tool:panel:gain_focus:' + self.handler.transId,
self.gain_focus, self
);
pgBrowser.Events.on(
'pgadmin:query_tool:panel:lost_focus:' + self.handler.transId,
self.lost_focus, self
);
}
// set focus on query tool once loaded
@ -422,6 +479,34 @@ define('tools.querytool', [
});
},
// This function will check the connection status at specific
// interval defined by the user in preference
checkConnectionStatus: function() {
var self = this,
preference = window.top.pgAdmin.Browser.get_preference(
'sqleditor', 'connection_status_fetch_time'
),
display_status = window.top.pgAdmin.Browser.get_preference(
'sqleditor', 'connection_status'
);
if(!preference && self.handler.is_new_browser_tab) {
preference = window.opener.pgAdmin.Browser.get_preference(
'sqleditor', 'connection_status_fetch_time'
);
display_status = window.opener.pgAdmin.Browser.get_preference(
'sqleditor', 'connection_status'
);
}
// Only enable pooling if it is enabled
if (display_status && display_status.value) {
SqlEditorUtils.updateConnectionStatus(
url_for('sqleditor.connection_status', {'trans_id': self.transId}),
preference.value
);
}
},
/* To prompt user for unsaved changes */
user_confirmation: function(panel, msg) {
// If there is anything to save then prompt user

View File

@ -268,6 +268,12 @@ module.exports = {
}, {
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader?name=fonts/[name].[ext]',
include: [
/node_modules/,
path.join(sourceDir, '/css/'),
path.join(sourceDir, '/scss/'),
path.join(sourceDir, '/fonts/'),
],
exclude: /vendor/,
}, {
test: /\.scss$/,