mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Allow the UI layout to be fully locked or to prevent docking changes. Fixes #2653
This commit is contained in:
parent
91075cc3f5
commit
2dd075161d
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 53 KiB |
BIN
docs/en_US/images/preferences_browser_display.png
Executable file → Normal file
BIN
docs/en_US/images/preferences_browser_display.png
Executable file → Normal file
Binary file not shown.
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 168 KiB |
@ -25,6 +25,9 @@ Use the *File* menu to access the following options:
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
| *Reset Layout* | If you have modified the workspace, click to restore the default layout. |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
| *Lock Layout* | Click to open a submenu to select the level for locking the UI layout |
|
||||
| | This can also be changed from browser display :ref:`preferences <preferences>` |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------+
|
||||
|
||||
The Object Menu
|
||||
***************
|
||||
|
@ -38,10 +38,6 @@ Use the fields on the *Display* panel to specify general display preferences:
|
||||
attempt to catch browser close or refresh events and prompt before allowing
|
||||
them to continue.
|
||||
|
||||
* When the *Show system objects?* switch is set to *True*, the client will
|
||||
display system objects such as system schemas (for example, *pg_temp*) or
|
||||
system columns (for example, *xmin* or *ctid*) in the tree control.
|
||||
|
||||
* When the *Enable browser tree animation?* switch is set to *True*, the client
|
||||
will display the animated tree control otherwise it will be unanimated.
|
||||
|
||||
@ -49,6 +45,23 @@ Use the fields on the *Display* panel to specify general display preferences:
|
||||
the client will display the animated dialogues/notifications otherwise it
|
||||
will be unanimated.
|
||||
|
||||
* Use the *Lock layout* field to lock the UI layout at different levels. This
|
||||
can also be changed from File menu on the :ref:`menu bar <menu_bar>`
|
||||
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| Option | Action |
|
||||
+=====================+===================================================================+
|
||||
| *None* | No locking. Every panel is resizable and dockable. |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| *Prevent docking* | This will disable the docking/undocking of the panels |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
| *Full* | This will disable resizing, docking/undocking of the panels |
|
||||
+---------------------+-------------------------------------------------------------------+
|
||||
|
||||
* When the *Show system objects?* switch is set to *True*, the client will
|
||||
display system objects such as system schemas (for example, *pg_temp*) or
|
||||
system columns (for example, *xmin* or *ctid*) in the tree control.
|
||||
|
||||
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the
|
||||
main window navigation:
|
||||
|
||||
|
@ -4,8 +4,12 @@ Version 4.8
|
||||
|
||||
Release date: 2019-06-27
|
||||
|
||||
This release contains a number of bug fixes since the release of pgAdmin4 4.7.
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin4 4.7.
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Feature #2653 <https://redmine.postgresql.org/issues/2653>`_ - Allow the UI layout to be fully locked or to prevent docking changes.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
@ -89,7 +89,7 @@
|
||||
"underscore": "^1.9.1",
|
||||
"underscore.string": "^3.3.5",
|
||||
"watchify": "~3.11.1",
|
||||
"webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d",
|
||||
"webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#f3c79e4b9a9e76a8e34736fc33473e65c524050c",
|
||||
"wkx": "^0.4.6"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -39,6 +39,7 @@ from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
from pgadmin.utils.csrf import pgCSRFProtect
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
from pgadmin.utils.menu import MenuItem
|
||||
from pgadmin.browser.register_browser_preferences import \
|
||||
register_browser_preferences
|
||||
from pgadmin.utils.master_password import validate_master_password, \
|
||||
@ -215,6 +216,40 @@ class BrowserModule(PgAdminModule):
|
||||
scripts.extend(module.get_own_javascripts())
|
||||
return scripts
|
||||
|
||||
def get_own_menuitems(self):
|
||||
return {
|
||||
'file_items': [
|
||||
MenuItem(
|
||||
name='mnu_locklayout',
|
||||
module='pgAdmin.Browser',
|
||||
label=gettext('Lock Layout'),
|
||||
priority=999,
|
||||
menu_items=[MenuItem(
|
||||
name='mnu_lock_none',
|
||||
module='pgAdmin.Browser',
|
||||
callback='mnu_lock_none',
|
||||
priority=0,
|
||||
label=gettext('None'),
|
||||
checked=True
|
||||
), MenuItem(
|
||||
name='mnu_lock_docking',
|
||||
module='pgAdmin.Browser',
|
||||
callback='mnu_lock_docking',
|
||||
priority=1,
|
||||
label=gettext('Prevent Docking'),
|
||||
checked=False
|
||||
), MenuItem(
|
||||
name='mnu_lock_full',
|
||||
module='pgAdmin.Browser',
|
||||
callback='mnu_lock_full',
|
||||
priority=2,
|
||||
label=gettext('Full Lock'),
|
||||
checked=False
|
||||
)]
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
def register_preferences(self):
|
||||
register_browser_preferences(self)
|
||||
|
||||
@ -226,7 +261,8 @@ class BrowserModule(PgAdminModule):
|
||||
return ['browser.index', 'browser.nodes',
|
||||
'browser.check_master_password',
|
||||
'browser.set_master_password',
|
||||
'browser.reset_master_password']
|
||||
'browser.reset_master_password',
|
||||
'browser.lock_layout']
|
||||
|
||||
|
||||
blueprint = BrowserModule(MODULE_NAME, __name__)
|
||||
@ -815,6 +851,21 @@ def set_master_password():
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/lock_layout", endpoint="lock_layout", methods=["PUT"])
|
||||
def lock_layout():
|
||||
data = None
|
||||
|
||||
if hasattr(request.data, 'decode'):
|
||||
data = request.data.decode('utf-8')
|
||||
|
||||
if data != '':
|
||||
data = json.loads(data)
|
||||
|
||||
blueprint.lock_layout.set(data['value'])
|
||||
|
||||
return make_json_response()
|
||||
|
||||
|
||||
# Only register route if SECURITY_CHANGEABLE is set to True
|
||||
# We can't access app context here so cannot
|
||||
# use app.config['SECURITY_CHANGEABLE']
|
||||
|
@ -8,6 +8,12 @@
|
||||
##########################################################################
|
||||
from flask_babelex import gettext
|
||||
|
||||
LOCK_LAYOUT_LEVEL = {
|
||||
'PREVENT_DOCKING': 'docking',
|
||||
'FULL': 'full',
|
||||
'NONE': 'none'
|
||||
}
|
||||
|
||||
|
||||
def register_browser_preferences(self):
|
||||
self.show_system_objects = self.preference.register(
|
||||
@ -58,6 +64,20 @@ def register_browser_preferences(self):
|
||||
)
|
||||
)
|
||||
|
||||
self.lock_layout = self.preference.register(
|
||||
'display', 'lock_layout',
|
||||
gettext('Lock Layout'), 'radioModern', LOCK_LAYOUT_LEVEL['NONE'],
|
||||
category_label=gettext('Display'), options=[
|
||||
{'label': gettext('None'), 'value': LOCK_LAYOUT_LEVEL['NONE']},
|
||||
{'label': gettext('Prevent Docking'),
|
||||
'value': LOCK_LAYOUT_LEVEL['PREVENT_DOCKING']},
|
||||
{'label': gettext('Full Lock'), 'value': LOCK_LAYOUT_LEVEL['FULL']},
|
||||
],
|
||||
help_str=gettext(
|
||||
'Lock the UI layout at different levels'
|
||||
)
|
||||
)
|
||||
|
||||
self.table_row_count_threshold = self.preference.register(
|
||||
'properties', 'table_row_count_threshold',
|
||||
gettext("Count rows if estimated less than"), 'integer', 2000,
|
||||
|
@ -15,7 +15,7 @@ define('pgadmin.browser', [
|
||||
'sources/csrf', 'sources/keyboard_shortcuts', 'pgadmin.browser.utils',
|
||||
'wcdocker', 'jquery.contextmenu', 'jquery.aciplugin', 'jquery.acitree',
|
||||
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
||||
'pgadmin.browser.menu', 'pgadmin.browser.panel',
|
||||
'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
||||
'pgadmin.browser.error', 'pgadmin.browser.frame',
|
||||
'pgadmin.browser.node', 'pgadmin.browser.collection',
|
||||
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||
@ -282,22 +282,6 @@ define('pgadmin.browser', [
|
||||
scripts[n].push({'name': m, 'path': p, loaded: false});
|
||||
},
|
||||
masterpass_callback_queue: [],
|
||||
// Build the default layout
|
||||
buildDefaultLayout: function(docker) {
|
||||
var browserPanel = docker.addPanel('browser', wcDocker.DOCK.LEFT);
|
||||
var dashboardPanel = docker.addPanel(
|
||||
'dashboard', wcDocker.DOCK.RIGHT, browserPanel);
|
||||
docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, {
|
||||
tabOrientation: wcDocker.TAB.TOP,
|
||||
});
|
||||
docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'statistics', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependencies', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependents', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
},
|
||||
// Enable/disable menu options
|
||||
enable_disable_menus: function(item) {
|
||||
// Mechanism to enable/disable menus depending on the condition.
|
||||
@ -354,35 +338,6 @@ define('pgadmin.browser', [
|
||||
$obj_mnu.append(create_submenu.$el);
|
||||
}
|
||||
},
|
||||
save_current_layout: function(layout_id, docker) {
|
||||
if(docker) {
|
||||
var layout = docker.save(),
|
||||
settings = { setting: layout_id, value: layout };
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url_for('settings.store_bulk'),
|
||||
data: settings,
|
||||
});
|
||||
}
|
||||
},
|
||||
restore_layout: function(docker, layout, defaultLayoutCallback) {
|
||||
// Try to restore the layout if there is one
|
||||
if (layout != '') {
|
||||
try {
|
||||
docker.restore(layout);
|
||||
}
|
||||
catch(err) {
|
||||
docker.clear();
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
},
|
||||
init: function() {
|
||||
var obj=this;
|
||||
if (obj.initialized) {
|
||||
@ -799,8 +754,8 @@ define('pgadmin.browser', [
|
||||
menus = pgMenu[a];
|
||||
}
|
||||
|
||||
if (!_.has(menus, m.name)) {
|
||||
menus[m.name] = new MenuItem({
|
||||
let get_menuitem_obj = function(m) {
|
||||
return new MenuItem({
|
||||
name: m.name, label: m.label, module: m.module,
|
||||
category: m.category, callback: m.callback,
|
||||
priority: m.priority, data: m.data, url: m.url || '#',
|
||||
@ -808,8 +763,21 @@ define('pgadmin.browser', [
|
||||
enable: (m.enable == '' ? true : (_.isString(m.enable) &&
|
||||
m.enable.toLowerCase() == 'false') ?
|
||||
false : m.enable),
|
||||
node: m.node,
|
||||
node: m.node, checked: m.checked,
|
||||
});
|
||||
};
|
||||
|
||||
if (!_.has(menus, m.name)) {
|
||||
menus[m.name] = get_menuitem_obj(m);
|
||||
|
||||
if(m.menu_items) {
|
||||
let sub_menu_items = [];
|
||||
|
||||
for(let i=0; i<m.menu_items.length; i++) {
|
||||
sub_menu_items.push(get_menuitem_obj(m.menu_items[i]));
|
||||
}
|
||||
menus[m.name]['menu_items'] = sub_menu_items;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
|
161
web/pgadmin/browser/static/js/layout.js
Normal file
161
web/pgadmin/browser/static/js/layout.js
Normal file
@ -0,0 +1,161 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import url_for from 'sources/url_for';
|
||||
import $ from 'jquery';
|
||||
import * as Alertify from 'pgadmin.alertifyjs';
|
||||
import gettext from 'sources/gettext';
|
||||
import 'wcdocker';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
var wcDocker = window.wcDocker;
|
||||
|
||||
/* Add cache related methods and properties */
|
||||
_.extend(pgBrowser, {
|
||||
lock_layout_levels : {
|
||||
PREVENT_DOCKING: 'docking',
|
||||
FULL: 'full',
|
||||
NONE: 'none',
|
||||
},
|
||||
|
||||
// Build the default layout
|
||||
buildDefaultLayout: function(docker) {
|
||||
var browserPanel = docker.addPanel('browser', wcDocker.DOCK.LEFT);
|
||||
var dashboardPanel = docker.addPanel(
|
||||
'dashboard', wcDocker.DOCK.RIGHT, browserPanel);
|
||||
docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, {
|
||||
tabOrientation: wcDocker.TAB.TOP,
|
||||
});
|
||||
docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'statistics', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependencies', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
docker.addPanel(
|
||||
'dependents', wcDocker.DOCK.STACKED, dashboardPanel);
|
||||
},
|
||||
|
||||
save_current_layout: function(layout_id, docker) {
|
||||
if(docker) {
|
||||
var layout = docker.save(),
|
||||
settings = { setting: layout_id, value: layout };
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url_for('settings.store_bulk'),
|
||||
data: settings,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
restore_layout: function(docker, layout, defaultLayoutCallback) {
|
||||
// Try to restore the layout if there is one
|
||||
if (layout != '') {
|
||||
try {
|
||||
docker.restore(layout);
|
||||
}
|
||||
catch(err) {
|
||||
docker.clear();
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(defaultLayoutCallback) {
|
||||
defaultLayoutCallback(docker);
|
||||
}
|
||||
}
|
||||
|
||||
/* preference available only with top/opener browser. */
|
||||
let browser = window.opener ?
|
||||
window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser;
|
||||
|
||||
/* interval required initially as preference load may take time */
|
||||
let cacheIntervalId = setInterval(()=> {
|
||||
let browserPref = browser.get_preferences_for_module('browser');
|
||||
if(browserPref) {
|
||||
clearInterval(cacheIntervalId);
|
||||
|
||||
browser.reflectLocklayoutChange(docker);
|
||||
|
||||
browser.onPreferencesChange('browser', function() {
|
||||
browser.reflectLocklayoutChange(docker);
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
|
||||
reflectLocklayoutChange: function(docker) {
|
||||
let browser = window.opener ?
|
||||
window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser;
|
||||
|
||||
let browserPref = browser.get_preferences_for_module('browser');
|
||||
browser.lock_layout(docker, browserPref.lock_layout);
|
||||
},
|
||||
|
||||
lock_layout: function(docker, op) {
|
||||
let menu_items = this.menus['file']['mnu_locklayout']['menu_items'];
|
||||
|
||||
switch(op) {
|
||||
case this.lock_layout_levels.PREVENT_DOCKING:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.PREVENT_DOCKING);
|
||||
break;
|
||||
case this.lock_layout_levels.FULL:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.FULL);
|
||||
break;
|
||||
case this.lock_layout_levels.NONE:
|
||||
docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
_.each(menu_items, function(menu_item) {
|
||||
if(menu_item.name != ('mnu_lock_'+op)) {
|
||||
menu_item.change_checked(false);
|
||||
} else {
|
||||
menu_item.change_checked(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
save_lock_layout: function(op) {
|
||||
let browser = window.opener ?
|
||||
window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser;
|
||||
|
||||
$.ajax({
|
||||
url: url_for('browser.lock_layout'),
|
||||
method: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
'value': op,
|
||||
}),
|
||||
}).done(function() {
|
||||
browser.cache_preferences('browser');
|
||||
}).fail(function(xhr, error) {
|
||||
Alertify.pgNotifier(error, xhr, gettext('Failed to save the lock layout setting.'));
|
||||
});
|
||||
},
|
||||
|
||||
mnu_lock_docking: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.PREVENT_DOCKING);
|
||||
this.save_lock_layout(this.lock_layout_levels.PREVENT_DOCKING);
|
||||
},
|
||||
|
||||
mnu_lock_full: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.FULL);
|
||||
this.save_lock_layout(this.lock_layout_levels.FULL);
|
||||
},
|
||||
|
||||
mnu_lock_none: function() {
|
||||
this.lock_layout(this.docker, this.lock_layout_levels.NONE);
|
||||
this.save_lock_layout(this.lock_layout_levels.NONE);
|
||||
},
|
||||
});
|
||||
|
||||
export {pgBrowser};
|
@ -19,6 +19,7 @@ define([
|
||||
var menu_opts = [
|
||||
'name', 'label', 'priority', 'module', 'callback', 'data', 'enable',
|
||||
'category', 'target', 'url' /* Do not show icon in the menus, 'icon' */ , 'node',
|
||||
'checked', 'menu_items',
|
||||
],
|
||||
defaults = {
|
||||
url: '#',
|
||||
@ -55,6 +56,17 @@ define([
|
||||
* Create the jquery element for the menu-item.
|
||||
*/
|
||||
create_el: function(node, item) {
|
||||
|
||||
if(this.menu_items) {
|
||||
_.each(this.menu_items, function(submenu_item){
|
||||
submenu_item.generate(node, item);
|
||||
});
|
||||
var create_submenu = pgAdmin.Browser.MenuGroup({
|
||||
'label': this.label,
|
||||
'id': this.name,
|
||||
}, this.menu_items);
|
||||
this.$el = create_submenu.$el;
|
||||
} else {
|
||||
var url = $('<a></a>', {
|
||||
'id': this.name,
|
||||
'href': this.url,
|
||||
@ -71,6 +83,10 @@ define([
|
||||
url.append($('<i></i>', {
|
||||
'class': this.icon,
|
||||
}));
|
||||
} else if(!_.isUndefined(this.checked)) {
|
||||
url.append($('<i></i>', {
|
||||
'class': 'fa fa-check '+ (this.checked?'':'visibility-hidden'),
|
||||
}));
|
||||
}
|
||||
|
||||
url.addClass((this.is_disabled ? ' disabled' : ''));
|
||||
@ -80,6 +96,8 @@ define([
|
||||
url.append(textSpan);
|
||||
|
||||
this.$el = $('<li/>').append(url);
|
||||
}
|
||||
|
||||
},
|
||||
/*
|
||||
* Updates the enable/disable state of the menu-item based on the current
|
||||
@ -154,6 +172,20 @@ define([
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/*
|
||||
* Change the checked value and update the checked icon on the menu
|
||||
*/
|
||||
change_checked(isChecked) {
|
||||
if(!_.isUndefined(this.checked)) {
|
||||
this.checked = isChecked;
|
||||
if(this.checked) {
|
||||
this.$el.find('.fa-check').removeClass('visibility-hidden');
|
||||
} else {
|
||||
this.$el.find('.fa-check').addClass('visibility-hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
|
@ -7,6 +7,32 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
{% macro A_MENU_ITEM(key, item) -%}
|
||||
{
|
||||
name: "{{ item.name }}",
|
||||
{% if item.module %}module: {{ item.module }},
|
||||
{% endif %}{% if item.url %}url: "{{ item.url }}",
|
||||
{% endif %}{% if item.target %}target: "{{ item.target }}",
|
||||
{% endif %}{% if item.callback %}callback: "{{ item.callback }}",
|
||||
{% endif %}{% if item.category %}category: "{{ item.category }}",
|
||||
{% endif %}{% if item.icon %}icon: '{{ item.icon }}',
|
||||
{% endif %}{% if item.data %}data: {{ item.data }},
|
||||
{% endif %}label: '{{ item.label }}', applies: ['{{ key.lower() }}'],
|
||||
priority: {{ item.priority }},
|
||||
enable: '{{ item.enable }}',
|
||||
{% if item.checked is defined %}checked: {% if item.checked %}true{% else %}false{% endif %},
|
||||
{% endif %}
|
||||
{% if item.menu_items %}menu_items: {{MENU_ITEMS(key, item.menu_items)}}
|
||||
{% endif %}
|
||||
}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro MENU_ITEMS(key, items) -%}
|
||||
[
|
||||
{% for item in items %}{% if loop.index != 1 %}, {% endif %}
|
||||
{{ A_MENU_ITEM(key, item) }}{% set hasMenus = True %}{% endfor %}
|
||||
]
|
||||
{%- endmacro %}
|
||||
|
||||
define('pgadmin.browser.utils',
|
||||
['sources/pgadmin'], function(pgAdmin) {
|
||||
@ -56,19 +82,7 @@ define('pgadmin.browser.utils',
|
||||
var self = this;
|
||||
if (this.counter.total == this.counter.loaded) {
|
||||
{% for key in ('File', 'Edit', 'Object' 'Tools', 'Management', 'Help') %}
|
||||
obj.add_menus([{% for item in current_app.menu_items['%s_items' % key.lower()] %}{% if loop.index != 1 %}, {% endif %}{
|
||||
name: "{{ item.name }}",
|
||||
{% if item.module %}module: {{ item.module }},
|
||||
{% endif %}{% if item.url %}url: "{{ item.url }}",
|
||||
{% endif %}{% if item.target %}target: "{{ item.target }}",
|
||||
{% endif %}{% if item.callback %}callback: "{{ item.callback }}",
|
||||
{% endif %}{% if item.category %}category: "{{ item.category }}",
|
||||
{% endif %}{% if item.icon %}icon: '{{ item.icon }}',
|
||||
{% endif %}{% if item.data %}data: {{ item.data }},
|
||||
{% endif %}label: '{{ item.label }}', applies: ['{{ key.lower() }}'],
|
||||
priority: {{ item.priority }},
|
||||
enable: '{{ item.enable }}'
|
||||
}{% set hasMenus = True %}{% endfor %}]);
|
||||
obj.add_menus({{ MENU_ITEMS(key, current_app.menu_items['%s_items' % key.lower()])}});
|
||||
{% endfor %}
|
||||
obj.create_menus();
|
||||
} else {
|
||||
|
@ -48,7 +48,7 @@ class PreferencesModule(PgAdminModule):
|
||||
return {
|
||||
'file_items': [
|
||||
MenuItem(name='mnu_preferences',
|
||||
priority=999,
|
||||
priority=997,
|
||||
module="pgAdmin.Preferences",
|
||||
callback='show',
|
||||
icon='fa fa-cog',
|
||||
|
@ -271,6 +271,8 @@ define('pgadmin.preferences', [
|
||||
return 'switch';
|
||||
case 'keyboardshortcut':
|
||||
return 'keyboardShortcut';
|
||||
case 'radioModern':
|
||||
return 'radioModern';
|
||||
default:
|
||||
if (console && console.warn) {
|
||||
// Warning for developer only.
|
||||
|
@ -39,7 +39,7 @@ class SettingsModule(PgAdminModule):
|
||||
'file_items': [
|
||||
MenuItem(
|
||||
name='mnu_resetlayout',
|
||||
priority=999,
|
||||
priority=998,
|
||||
module="pgAdmin.Settings",
|
||||
callback='show',
|
||||
icon='fa fa-retweet',
|
||||
|
@ -60,6 +60,7 @@ define([
|
||||
'select2': 'select2',
|
||||
'note': 'note',
|
||||
'color': 'color',
|
||||
'radioModern': 'radioModern',
|
||||
};
|
||||
|
||||
Backform.getMappedControl = function(type, mode) {
|
||||
@ -95,6 +96,17 @@ define([
|
||||
return type;
|
||||
};
|
||||
|
||||
/* Returns raw data as it is */
|
||||
var RawFormatter = Backform.RawFormatter = function() {};
|
||||
_.extend(RawFormatter.prototype, {
|
||||
fromRaw: function(rawData) {
|
||||
return rawData;
|
||||
},
|
||||
toRaw: function(formattedData) {
|
||||
return formattedData;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var BackformControlInit = Backform.Control.prototype.initialize,
|
||||
BackformControlRemove = Backform.Control.prototype.remove;
|
||||
@ -422,6 +434,48 @@ define([
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Backform.RadioModernControl = Backform.RadioControl.extend({
|
||||
defaults: {
|
||||
controlLabelClassName: Backform.controlLabelClassName,
|
||||
controlsClassName: Backform.controlsClassName,
|
||||
extraClasses: [],
|
||||
helpMessage: '',
|
||||
name: '',
|
||||
},
|
||||
template: _.template([
|
||||
'<label class="<%=controlLabelClassName%>"><%=label%></label>',
|
||||
'<div class="<%=controlsClassName%> <%=extraClasses.join(\' \')%>">',
|
||||
' <div class="btn-group pgadmin-controls-radio-none" data-toggle="buttons">',
|
||||
' <% for (var i=0; i < options.length; i++) { %>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <label class="btn btn-primary<% if (option.value == value) { %> active<%}%>" tabindex="0">',
|
||||
' <input type="radio" name="<%=name%>" autocomplete="off" value=<%-formatter.fromRaw(option.value)%> <% if (option.value == value) { %> checked<%}%> > <%-option.label%>',
|
||||
' </label>',
|
||||
' <% } %>',
|
||||
' </div>',
|
||||
' <% if (helpMessage && helpMessage.length) { %>',
|
||||
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
' <% } %>',
|
||||
'</div>',
|
||||
].join('\n')),
|
||||
formatter: RawFormatter,
|
||||
getValueFromDOM: function() {
|
||||
return this.formatter.toRaw(this.$el.find('input[type="radio"]:checked').attr('value'), this.model);
|
||||
},
|
||||
render: function() {
|
||||
Backform.RadioControl.prototype.render.apply(this, arguments);
|
||||
this.$el.find('.btn').on('keyup', (e)=>{
|
||||
switch(e.keyCode) {
|
||||
case 32: /* Spacebar click */
|
||||
$(e.currentTarget).trigger('click');
|
||||
break;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
// Requires the Bootstrap Switch to work.
|
||||
Backform.SwitchControl = Backform.InputControl.extend({
|
||||
defaults: {
|
||||
|
@ -310,3 +310,8 @@ td.switch-cell > div.toggle {
|
||||
.btn.disabled, .btn:disabled, .btn[disabled] {
|
||||
opacity: $btn-disabled-opacity;
|
||||
}
|
||||
|
||||
.btn-group label.btn.btn-primary.active {
|
||||
background-color: $color-primary-light;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
@ -51,8 +51,11 @@ define([
|
||||
id: 'op',
|
||||
label: gettext('Maintenance operation'),
|
||||
cell: 'string',
|
||||
type: 'text',
|
||||
type: 'radioModern',
|
||||
controlsClassName: 'pgadmin-controls col-12 col-sm-8',
|
||||
controlLabelClassName: 'control-label col-sm-4 col-12',
|
||||
group: gettext('Options'),
|
||||
value: 'VACUUM',
|
||||
options: [{
|
||||
'label': 'VACUUM',
|
||||
'value': 'VACUUM',
|
||||
@ -70,30 +73,6 @@ define([
|
||||
'value': 'CLUSTER',
|
||||
},
|
||||
],
|
||||
control: Backform.RadioControl.extend({
|
||||
template: _.template([
|
||||
'<label class="control-label col-sm-4 col-12"><%=label%></label>',
|
||||
'<div class="pgadmin-controls col-12 col-sm-8 btn-group pg-maintenance-op pgadmin-controls-radio-none" data-toggle="buttons">',
|
||||
' <% for (var i=0; i < options.length; i++) { %>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <label class="btn btn-primary<% if (i == 0) { %> active<%}%>" tabindex="0">',
|
||||
' <input type="radio" name="op" id="op" autocomplete="off" value=<%-formatter.fromRaw(option.value)%><% if (i == 0) { %> selected<%}%> > <%-option.label%>',
|
||||
' </label>',
|
||||
' <% } %>',
|
||||
'</div>',
|
||||
].join('\n')),
|
||||
render: function() {
|
||||
Backform.RadioControl.prototype.render.apply(this, arguments);
|
||||
this.$el.find('.pg-maintenance-op .btn').on('keyup', (e)=>{
|
||||
switch(e.keyCode) {
|
||||
case 32: /* Spacebar click */
|
||||
$(e.currentTarget).trigger('click');
|
||||
break;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'nested',
|
||||
|
@ -1,4 +0,0 @@
|
||||
.btn-group.pg-maintenance-op label.btn.btn-primary.active {
|
||||
background-color: $color-primary-light;
|
||||
color: $color-primary;
|
||||
}
|
@ -428,7 +428,7 @@ class Preferences(object):
|
||||
assert _type is not None, "Type for a preference cannot be none!"
|
||||
assert _type in (
|
||||
'boolean', 'integer', 'numeric', 'date', 'datetime',
|
||||
'options', 'multiline', 'switch', 'node', 'text',
|
||||
'options', 'multiline', 'switch', 'node', 'text', 'radioModern',
|
||||
'keyboardshortcut'
|
||||
), "Type cannot be found in the defined list!"
|
||||
|
||||
|
109
web/regression/javascript/browser/layout_spec.js
Normal file
109
web/regression/javascript/browser/layout_spec.js
Normal file
@ -0,0 +1,109 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import {pgBrowser} from 'pgadmin.browser.layout';
|
||||
import 'wcdocker';
|
||||
|
||||
var wcDocker = window.wcDocker;
|
||||
|
||||
describe('layout related functions test', function() {
|
||||
let menu_items = null;
|
||||
let dummy_cache = [{
|
||||
id: 2,
|
||||
mid: 1,
|
||||
module:'browser',
|
||||
name:'lock_layout',
|
||||
value: 'none',
|
||||
}];
|
||||
|
||||
beforeEach(function(){
|
||||
pgBrowser.preferences_cache = dummy_cache;
|
||||
pgBrowser.docker = {
|
||||
'lockLayout': ()=>{},
|
||||
};
|
||||
|
||||
_.extend(pgBrowser,{
|
||||
'menus': {
|
||||
'file': {
|
||||
'mnu_locklayout': {
|
||||
'menu_items': [
|
||||
{'name': 'mnu_lock_none', change_checked: ()=> {}},
|
||||
{'name': 'mnu_lock_docking', change_checked: ()=> {}},
|
||||
{'name': 'mnu_lock_full', change_checked: ()=> {}},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
menu_items = pgBrowser.menus.file.mnu_locklayout.menu_items;
|
||||
});
|
||||
|
||||
describe('for menu actions', function() {
|
||||
beforeEach(function(){
|
||||
spyOn(pgBrowser, 'lock_layout');
|
||||
spyOn(pgBrowser, 'save_lock_layout');
|
||||
});
|
||||
|
||||
it('mnu_lock_none', function() {
|
||||
pgBrowser.mnu_lock_none();
|
||||
expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'none');
|
||||
expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('none');
|
||||
});
|
||||
|
||||
it('mnu_lock_docking', function() {
|
||||
pgBrowser.mnu_lock_docking();
|
||||
expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'docking');
|
||||
expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('docking');
|
||||
});
|
||||
|
||||
it('mnu_lock_full', function() {
|
||||
pgBrowser.mnu_lock_full();
|
||||
expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'full');
|
||||
expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('full');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lock_layout', function() {
|
||||
let change_checked_test= function(menu_name) {
|
||||
for(let i=0; i<menu_items.length; i++) {
|
||||
if(menu_items[i].name == menu_name) {
|
||||
expect(menu_items[i].change_checked).toHaveBeenCalledWith(true);
|
||||
} else {
|
||||
expect(menu_items[i].change_checked).toHaveBeenCalledWith(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(function(){
|
||||
spyOn(pgBrowser.docker, 'lockLayout');
|
||||
for(let i=0; i<menu_items.length; i++) {
|
||||
spyOn(menu_items[i], 'change_checked');
|
||||
}
|
||||
});
|
||||
|
||||
it('none', function() {
|
||||
pgBrowser.lock_layout(pgBrowser.docker, 'none');
|
||||
expect(pgBrowser.docker.lockLayout).toHaveBeenCalledWith(wcDocker.LOCK_LAYOUT_LEVEL.NONE);
|
||||
change_checked_test('mnu_lock_none');
|
||||
});
|
||||
|
||||
it('docking', function() {
|
||||
pgBrowser.lock_layout(pgBrowser.docker, 'docking');
|
||||
expect(pgBrowser.docker.lockLayout).toHaveBeenCalledWith(wcDocker.LOCK_LAYOUT_LEVEL.PREVENT_DOCKING);
|
||||
change_checked_test('mnu_lock_docking');
|
||||
});
|
||||
|
||||
it('full', function() {
|
||||
pgBrowser.lock_layout(pgBrowser.docker, 'full');
|
||||
expect(pgBrowser.docker.lockLayout).toHaveBeenCalledWith(wcDocker.LOCK_LAYOUT_LEVEL.FULL);
|
||||
change_checked_test('mnu_lock_full');
|
||||
});
|
||||
});
|
||||
});
|
@ -190,6 +190,7 @@ var webpackShimConfig = {
|
||||
'pgadmin.browser.error': path.join(__dirname, './pgadmin/browser/static/js/error'),
|
||||
'pgadmin.browser.frame': path.join(__dirname, './pgadmin/browser/static/js/frame'),
|
||||
'pgadmin.browser.keyboard': path.join(__dirname, './pgadmin/browser/static/js/keyboard'),
|
||||
'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
|
||||
'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
|
||||
'pgadmin.browser.menu': path.join(__dirname, './pgadmin/browser/static/js/menu'),
|
||||
'pgadmin.browser.messages': '/browser/js/messages',
|
||||
|
@ -72,6 +72,7 @@ module.exports = {
|
||||
alias: {
|
||||
'top': path.join(__dirname, './pgadmin'),
|
||||
'jquery': path.join(__dirname, './node_modules/jquery/dist/jquery'),
|
||||
'wcdocker': path.join(__dirname, './node_modules/webcabin-docker/Build/wcDocker'),
|
||||
'alertify': path.join(__dirname, './node_modules/alertifyjs/build/alertify'),
|
||||
'jquery.event.drag': path.join(__dirname, './node_modules/slickgrid/lib/jquery.event.drag-2.3.0'),
|
||||
'jquery.ui': path.join(__dirname, './node_modules/slickgrid/lib/jquery-ui-1.11.3'),
|
||||
@ -100,6 +101,7 @@ module.exports = {
|
||||
'pgadmin.backform': sourcesDir + '/js/backform.pgadmin',
|
||||
'pgbrowser': path.resolve(__dirname, 'regression/javascript/fake_browser'),
|
||||
'pgadmin.schema.dir': path.resolve(__dirname, 'pgadmin/browser/server_groups/servers/databases/schemas/static/js'),
|
||||
'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
|
||||
'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
|
||||
'bundled_codemirror': path.join(__dirname, './pgadmin/static/bundle/codemirror'),
|
||||
},
|
||||
|
@ -8508,9 +8508,9 @@ watchpack@^1.5.0:
|
||||
graceful-fs "^4.1.2"
|
||||
neo-async "^2.5.0"
|
||||
|
||||
"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d":
|
||||
"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#f3c79e4b9a9e76a8e34736fc33473e65c524050c":
|
||||
version "2.2.4-dev"
|
||||
resolved "git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d"
|
||||
resolved "git+https://github.com/EnterpriseDB/wcDocker/#f3c79e4b9a9e76a8e34736fc33473e65c524050c"
|
||||
dependencies:
|
||||
FileSaver "^0.10.0"
|
||||
font-awesome "^4.7.0"
|
||||
|
Loading…
Reference in New Issue
Block a user