Allow the UI layout to be fully locked or to prevent docking changes. Fixes #2653

This commit is contained in:
Dave Page 2019-05-31 11:51:30 -04:00
parent 91075cc3f5
commit 2dd075161d
24 changed files with 539 additions and 125 deletions

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 168 KiB

View File

@ -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
***************

View File

@ -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:

View File

@ -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
*********

View File

@ -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": {

View File

@ -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']

View File

@ -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,

View File

@ -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(

View 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};

View File

@ -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');
}
}
},
});
/*

View File

@ -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 {

View File

@ -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',

View File

@ -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.

View File

@ -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',

View File

@ -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: {

View File

@ -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;
}

View File

@ -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',

View File

@ -1,4 +0,0 @@
.btn-group.pg-maintenance-op label.btn.btn-primary.active {
background-color: $color-primary-light;
color: $color-primary;
}

View File

@ -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!"

View 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');
});
});
});

View File

@ -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',

View File

@ -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'),
},

View File

@ -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"