diff --git a/docs/en_US/release_notes_4_4.rst b/docs/en_US/release_notes_4_4.rst index e380f3418..71e7d2232 100644 --- a/docs/en_US/release_notes_4_4.rst +++ b/docs/en_US/release_notes_4_4.rst @@ -54,6 +54,7 @@ Bug fixes | `Bug #4071 `_ - Ensure that Firefox prompts for a filename/location when downloading query results as a CSV file. | `Bug #4073 `_ - Change the CodeMirror active line background colour to $color-danger-lighter so it doesn't conflict with the selection colour. | `Bug #4081 `_ - Fix the RE-SQL syntax for roles with a VALID UNTIL clause. +| `Bug #4084 `_ - Overhaul the layout saving code so it includes the Query Tool and Debugger, and stores the layout when change events are detected rather than (unreliably) on exit. | `Bug #4090 `_ - Improve the German translation for Backup Server. | `Bug #4099 `_ - Fix SQL help for EPAS 10+, and refactor the URL generation code into a testable function. | `Bug #4100 `_ - Ensure sequences can be created with increment, start, minimum and maximum options set. diff --git a/web/package.json b/web/package.json index e6424f4fc..64dd85778 100644 --- a/web/package.json +++ b/web/package.json @@ -90,7 +90,7 @@ "underscore": "^1.9.1", "underscore.string": "^3.3.5", "watchify": "~3.11.1", - "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#32b58375ac57c8b7c681a64fea226ce306dee33a", + "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d", "wkx": "^0.4.6" }, "scripts": { diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js index 34c90cf47..8774277f5 100644 --- a/web/pgadmin/browser/static/js/browser.js +++ b/web/pgadmin/browser/static/js/browser.js @@ -280,19 +280,19 @@ define('pgadmin.browser', [ scripts[n].push({'name': m, 'path': p, loaded: false}); }, // Build the default layout - buildDefaultLayout: function() { - var browserPanel = this.docker.addPanel('browser', wcDocker.DOCK.LEFT); - var dashboardPanel = this.docker.addPanel( + buildDefaultLayout: function(docker) { + var browserPanel = docker.addPanel('browser', wcDocker.DOCK.LEFT); + var dashboardPanel = docker.addPanel( 'dashboard', wcDocker.DOCK.RIGHT, browserPanel); - this.docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, { + docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, { tabOrientation: wcDocker.TAB.TOP, }); - this.docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel); - this.docker.addPanel( + docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel); + docker.addPanel( 'statistics', wcDocker.DOCK.STACKED, dashboardPanel); - this.docker.addPanel( + docker.addPanel( 'dependencies', wcDocker.DOCK.STACKED, dashboardPanel); - this.docker.addPanel( + docker.addPanel( 'dependents', wcDocker.DOCK.STACKED, dashboardPanel); }, // Enable/disable menu options @@ -351,10 +351,10 @@ define('pgadmin.browser', [ $obj_mnu.append(create_submenu.$el); } }, - save_current_layout: function(obj) { - if(obj.docker) { - var state = obj.docker.save(); - var settings = { setting: 'Browser/Layout', value: state }; + save_current_layout: function(layout_id, docker) { + if(docker) { + var layout = docker.save(); + var settings = { setting: layout_id, value: layout }; $.ajax({ type: 'POST', url: url_for('settings.store_bulk'), @@ -362,6 +362,24 @@ define('pgadmin.browser', [ }); } }, + 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) { @@ -395,33 +413,10 @@ define('pgadmin.browser', [ // Stored layout in database from the previous session var layout = pgBrowser.utils.layout; + obj.restore_layout(obj.docker, layout, obj.buildDefaultLayout.bind(obj)); - // Try to restore the layout if there is one - if (layout != '') { - try { - obj.docker.restore(layout); - } - catch(err) { - obj.docker.clear(); - obj.buildDefaultLayout(); - } - } else { - obj.buildDefaultLayout(); - } - - // Listen to panel attach/detach event so that last layout will be remembered - _.each(obj.panels, function(panel) { - if (panel.panel) { - panel.panel.on(wcDocker.EVENT.ATTACHED, function() { - obj.save_current_layout(obj); - }); - panel.panel.on(wcDocker.EVENT.DETACHED, function() { - obj.save_current_layout(obj); - }); - panel.panel.on(wcDocker.EVENT.MOVE_ENDED, function() { - obj.save_current_layout(obj); - }); - } + obj.docker.on(wcDocker.EVENT.LAYOUT_CHANGED, function() { + obj.save_current_layout('Browser/Layout', obj.docker); }); } diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index ae70cd641..10427f0e0 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -487,6 +487,7 @@ define('pgadmin.browser.node', [ showTitle: true, isCloseable: true, isPrivate: true, + isLayoutMember: false, elContainer: true, content: '
' + gettext('Please wait while we fetch information about the node from the server...') + '
', onCreate: function(myPanel, $container) { diff --git a/web/pgadmin/browser/static/js/panel.js b/web/pgadmin/browser/static/js/panel.js index 8fbf2e13f..23645c5e2 100644 --- a/web/pgadmin/browser/static/js/panel.js +++ b/web/pgadmin/browser/static/js/panel.js @@ -17,7 +17,7 @@ define( pgAdmin.Browser.Panel = function(options) { var defaults = [ 'name', 'title', 'width', 'height', 'showTitle', 'isCloseable', - 'isPrivate', 'content', 'icon', 'events', 'onCreate', 'elContainer', + 'isPrivate', 'isLayoutMember', 'content', 'icon', 'events', 'onCreate', 'elContainer', 'canHide', 'limit', 'extraClasses', ]; _.extend(this, _.pick(options, defaults)); @@ -31,6 +31,7 @@ define( showTitle: true, isCloseable: true, isPrivate: false, + isLayoutMember: true, content: '', icon: '', panel: null, @@ -45,6 +46,7 @@ define( title: that.title, isPrivate: that.isPrivate, limit: that.limit, + isLayoutMember: that.isLayoutMember, onCreate: function(myPanel) { $(myPanel).data('pgAdminName', that.name); myPanel.initSize(that.width, that.height); @@ -187,12 +189,10 @@ define( return; if (eventName == 'panelClosed') { - pgBrowser.save_current_layout(pgBrowser); /* Pass the closed flag also */ pgAdmin.Dashboard.toggleVisibility(false, true); } else if (eventName == 'panelVisibilityChanged') { if (pgBrowser.tree) { - pgBrowser.save_current_layout(pgBrowser); var selectedNode = pgBrowser.tree.selected(); if (!_.isUndefined(pgAdmin.Dashboard)) { pgAdmin.Dashboard.toggleVisibility(pgBrowser.panels.dashboard.panel.isVisible()); diff --git a/web/pgadmin/settings/__init__.py b/web/pgadmin/settings/__init__.py index 63227d82a..fd79634eb 100644 --- a/web/pgadmin/settings/__init__.py +++ b/web/pgadmin/settings/__init__.py @@ -71,7 +71,7 @@ def store_setting(setting, value): db.session.commit() -def get_setting(setting, default=None): +def get_setting(setting, default=''): """Retrieve a configuration setting for the current user, or return the default value specified by the caller.""" data = Setting.query.filter_by( @@ -137,12 +137,15 @@ def store(setting=None, value=None): def reset_layout(): """Reset configuration setting""" - # There can be only one record at most - data = Setting.query.filter_by(user_id=current_user.id).first() try: - if data is not None: - db.session.delete(data) - db.session.commit() + db.session.query(Setting) \ + .filter(Setting.user_id == current_user.id)\ + .filter((Setting.setting == 'Browser/Layout') | + (Setting.setting == 'SQLEditor/Layout') | + (Setting.setting == 'Debugger/Layout'))\ + .delete() + + db.session.commit() except Exception as e: return make_json_response( status=410, success=0, errormsg=str(e) diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py index c990eb296..05e723ea7 100644 --- a/web/pgadmin/tools/datagrid/__init__.py +++ b/web/pgadmin/tools/datagrid/__init__.py @@ -29,6 +29,7 @@ from pgadmin.model import Server from pgadmin.utils.driver import get_driver from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost from pgadmin.utils.preferences import Preferences +from pgadmin.settings import get_setting query_tool_close_session_lock = Lock() @@ -283,6 +284,8 @@ def panel(trans_id, is_query_tool, editor_title): bgcolor = s.bgcolor fgcolor = s.fgcolor or 'black' + layout = get_setting('SQLEditor/Layout') + url_params = dict() if is_query_tool == 'true': url_params['sgid'] = trans_obj.sgid @@ -309,7 +312,8 @@ def panel(trans_id, is_query_tool, editor_title): client_platform=user_agent.platform, bgcolor=bgcolor, fgcolor=fgcolor, - url_params=json.dumps(url_params) + url_params=json.dumps(url_params), + layout=layout, ) diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html index 067cd7a26..594608310 100644 --- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html +++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html @@ -399,7 +399,8 @@ "{{ editor_title }}", script_type_url, "{{ server_type }}", - {{ url_params|safe}} + {{ url_params|safe}}, + '{{ layout|safe }}' ); }); {% endblock %} diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py index 6ea816c3e..44d55da8c 100644 --- a/web/pgadmin/tools/debugger/__init__.py +++ b/web/pgadmin/tools/debugger/__init__.py @@ -29,6 +29,7 @@ from pgadmin.utils.ajax import bad_request from pgadmin.utils.ajax import make_json_response, \ internal_server_error from pgadmin.utils.driver import get_driver +from pgadmin.settings import get_setting from config import PG_DEFAULT_DRIVER from pgadmin.model import db, DebuggerFunctionArguments @@ -589,6 +590,8 @@ def direct_new(trans_id): function_arguments += ')' + layout = get_setting('Debugger/Layout') + return render_template( "debugger/direct.html", _=gettext, @@ -598,7 +601,8 @@ def direct_new(trans_id): is_desktop_mode=current_app.PGADMIN_RUNTIME, is_linux=is_linux_platform, client_platform=user_agent.platform, - function_name_with_arguments=obj['function_name'] + function_arguments + function_name_with_arguments=obj['function_name'] + function_arguments, + layout=layout ) diff --git a/web/pgadmin/tools/debugger/static/js/direct.js b/web/pgadmin/tools/debugger/static/js/direct.js index 494291f43..2a9347e6f 100644 --- a/web/pgadmin/tools/debugger/static/js/direct.js +++ b/web/pgadmin/tools/debugger/static/js/direct.js @@ -1534,7 +1534,7 @@ define([ _.extend(DirectDebug.prototype, { /* We should get the transaction id from the server during initialization here */ - init: function(trans_id, debug_type, function_name_with_arguments) { + load: function(trans_id, debug_type, function_name_with_arguments, layout) { // We do not want to initialize the module multiple times. var self = this; _.bindAll(pgTools.DirectDebug, 'messages'); @@ -1554,6 +1554,7 @@ define([ this.is_user_aborted_debugging = false; this.is_polling_required = true; // Flag to stop unwanted ajax calls this.function_name_with_arguments = function_name_with_arguments; + this.layout = layout; let browser = window.opener ? window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser; @@ -1710,6 +1711,18 @@ define([ } }, + buildDefaultLayout: function(docker) { + let code_editor_panel = docker.addPanel('code', wcDocker.DOCK.TOP); + + let parameters_panel = docker.addPanel('parameters', wcDocker.DOCK.BOTTOM, code_editor_panel); + docker.addPanel('local_variables',wcDocker.DOCK.STACKED, parameters_panel, { + tabOrientation: wcDocker.TAB.TOP, + }); + docker.addPanel('messages', wcDocker.DOCK.STACKED, parameters_panel); + docker.addPanel('results', wcDocker.DOCK.STACKED, parameters_panel); + docker.addPanel('stack_pane', wcDocker.DOCK.STACKED, parameters_panel); + }, + // Create the debugger layout with splitter and display the appropriate data received from server. intializePanels: function() { var self = this; @@ -1780,23 +1793,19 @@ define([ stack_pane.load(self.docker); }); - self.code_editor_panel = self.docker.addPanel('code', wcDocker.DOCK.TOP); - self.parameters_panel = self.docker.addPanel( - 'parameters', wcDocker.DOCK.BOTTOM, self.code_editor_panel - ); - self.local_variables_panel = self.docker.addPanel( - 'local_variables', - wcDocker.DOCK.STACKED, - self.parameters_panel, { - tabOrientation: wcDocker.TAB.TOP, - } - ); - self.messages_panel = self.docker.addPanel( - 'messages', wcDocker.DOCK.STACKED, self.parameters_panel); - self.results_panel = self.docker.addPanel( - 'results', wcDocker.DOCK.STACKED, self.parameters_panel); - self.stack_pane_panel = self.docker.addPanel( - 'stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel); + // restore the layout if present else fallback to buildDefaultLayout + pgBrowser.restore_layout(self.docker, self.layout, this.buildDefaultLayout.bind(this)); + + self.docker.on(wcDocker.EVENT.LAYOUT_CHANGED, function() { + pgBrowser.save_current_layout('Debugger/Layout', self.docker); + }); + + self.code_editor_panel = self.docker.findPanels('code')[0]; + self.parameters_panel = self.docker.findPanels('parameters')[0]; + self.local_variables_panel = self.docker.findPanels('local_variables')[0]; + self.messages_panel = self.docker.findPanels('messages')[0]; + self.results_panel = self.docker.findPanels('results')[0]; + self.stack_pane_panel = self.docker.findPanels('stack_pane')[0]; var editor_pane = $('
'); diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html b/web/pgadmin/tools/debugger/templates/debugger/direct.html index da8fe4464..d5a97138b 100644 --- a/web/pgadmin/tools/debugger/templates/debugger/direct.html +++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html @@ -9,7 +9,7 @@ try { var pgDirectDebug = pgDirectDebug || pgAdmin.Tools.DirectDebug; var $ = pgDirectDebug.jquery; - pgDirectDebug.init({{ uniqueId }}, {{ debug_type }}, '{{ function_name_with_arguments }}'); + pgDirectDebug.load({{ uniqueId }}, {{ debug_type }}, '{{ function_name_with_arguments }}', '{{layout|safe}}'); window.onbeforeunload = function(ev) { $.ajax({ url: "{{ url_for('debugger.index') }}close/{{ uniqueId }}", diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index c908f52b7..6da106c1e 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -76,6 +76,7 @@ define('tools.querytool', [ this.preferences = browser.get_preferences_for_module('sqleditor'); this.handler.preferences = this.preferences; this.connIntervalId = null; + this.layout = opts.layout; }, // Bind all the events @@ -149,6 +150,18 @@ define('tools.querytool', [ } }, + buildDefaultLayout: function(docker) { + let sql_panel_obj = docker.addPanel('sql_panel', wcDocker.DOCK.TOP); + + docker.addPanel('scratch', wcDocker.DOCK.RIGHT, sql_panel_obj); + docker.addPanel('history', wcDocker.DOCK.STACKED, sql_panel_obj); + + let data_output_panel = docker.addPanel('data_output', wcDocker.DOCK.BOTTOM); + docker.addPanel('explain', wcDocker.DOCK.STACKED, data_output_panel); + docker.addPanel('messages', wcDocker.DOCK.STACKED, data_output_panel); + docker.addPanel('notifications', wcDocker.DOCK.STACKED, data_output_panel); + }, + // This function is used to render the template. render: function() { var self = this; @@ -170,7 +183,7 @@ define('tools.querytool', [ // Create main wcDocker instance - var main_docker = new wcDocker( + self.docker = new wcDocker( '#editor-panel', { allowContextMenu: true, allowCollapse: false, @@ -181,8 +194,8 @@ define('tools.querytool', [ theme: 'webcabin.overrides.css', }); - self.docker = main_docker; + // Create the panels var sql_panel = new pgAdmin.Browser.Panel({ name: 'sql_panel', title: gettext('Query Editor'), @@ -192,45 +205,6 @@ define('tools.querytool', [ isPrivate: true, }); - sql_panel.load(main_docker); - self.sql_panel_obj = main_docker.addPanel('sql_panel', wcDocker.DOCK.TOP); - - var text_container = $(''); - var output_container = $('
').append(text_container); - self.sql_panel_obj.$container.find('.pg-panel-content').append(output_container); - - self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), { - tabindex: '0', - lineNumbers: true, - styleSelectedText: true, - mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql', - foldOptions: { - widget: '\u2026', - }, - foldGutter: { - rangeFinder: CodeMirror.fold.combine( - CodeMirror.pgadminBeginRangeFinder, - CodeMirror.pgadminIfRangeFinder, - CodeMirror.pgadminLoopRangeFinder, - CodeMirror.pgadminCaseRangeFinder - ), - }, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - extraKeys: pgBrowser.editor_shortcut_keys, - scrollbarStyle: 'simple', - }); - - // Refresh Code mirror on SQL panel resize to - // display its value properly - self.sql_panel_obj.on(wcDocker.EVENT.RESIZE_ENDED, function() { - setTimeout(function() { - if (self && self.query_tool_obj) { - self.query_tool_obj.refresh(); - } - }, 200); - }); - - // Create panels for 'Data Output', 'Explain', 'Messages', 'History' and 'Geometry Viewer' var data_output = new pgAdmin.Browser.Panel({ name: 'data_output', title: gettext('Data Output'), @@ -303,25 +277,67 @@ define('tools.querytool', [ }); // Load all the created panels - data_output.load(main_docker); - explain.load(main_docker); - messages.load(main_docker); - history.load(main_docker); - scratch.load(main_docker); - notifications.load(main_docker); - geometry_viewer.load(main_docker); + sql_panel.load(self.docker); + data_output.load(self.docker); + explain.load(self.docker); + messages.load(self.docker); + history.load(self.docker); + scratch.load(self.docker); + notifications.load(self.docker); + geometry_viewer.load(self.docker); - // Add all the panels to the docker - self.scratch_panel = main_docker.addPanel('scratch', wcDocker.DOCK.RIGHT, self.sql_panel_obj); - self.history_panel = main_docker.addPanel('history', wcDocker.DOCK.STACKED, self.sql_panel_obj); - self.data_output_panel = main_docker.addPanel('data_output', wcDocker.DOCK.BOTTOM); - self.explain_panel = main_docker.addPanel('explain', wcDocker.DOCK.STACKED, self.data_output_panel); - self.messages_panel = main_docker.addPanel('messages', wcDocker.DOCK.STACKED, self.data_output_panel); - self.notifications_panel = main_docker.addPanel('notifications', wcDocker.DOCK.STACKED, self.data_output_panel); + // restore the layout if present else fallback to buildDefaultLayout + pgBrowser.restore_layout(self.docker, this.layout, this.buildDefaultLayout.bind(this)); + + self.docker.on(wcDocker.EVENT.LAYOUT_CHANGED, function() { + pgBrowser.save_current_layout('SQLEditor/Layout', self.docker); + }); + + self.sql_panel_obj = self.docker.findPanels('sql_panel')[0]; + self.history_panel = self.docker.findPanels('history')[0]; + self.data_output_panel = self.docker.findPanels('data_output')[0]; + self.explain_panel = self.docker.findPanels('explain')[0]; + self.messages_panel = self.docker.findPanels('messages')[0]; + self.notifications_panel = self.docker.findPanels('notifications')[0]; + + // Refresh Code mirror on SQL panel resize to + // display its value properly + self.sql_panel_obj.on(wcDocker.EVENT.RESIZE_ENDED, function() { + setTimeout(function() { + if (self && self.query_tool_obj) { + self.query_tool_obj.refresh(); + } + }, 200); + }); self.render_history_grid(); queryToolNotifications.renderNotificationsGrid(self.notifications_panel); + var text_container = $(''); + var output_container = $('
').append(text_container); + self.sql_panel_obj.$container.find('.pg-panel-content').append(output_container); + + self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), { + tabindex: '0', + lineNumbers: true, + styleSelectedText: true, + mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql', + foldOptions: { + widget: '\u2026', + }, + foldGutter: { + rangeFinder: CodeMirror.fold.combine( + CodeMirror.pgadminBeginRangeFinder, + CodeMirror.pgadminIfRangeFinder, + CodeMirror.pgadminLoopRangeFinder, + CodeMirror.pgadminCaseRangeFinder + ), + }, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + extraKeys: pgBrowser.editor_shortcut_keys, + scrollbarStyle: 'simple', + }); + if (!self.preferences.new_browser_tab) { // Listen on the panel closed event and notify user to save modifications. _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) { @@ -2066,7 +2082,7 @@ define('tools.querytool', [ * header and loading icon and start execution of the sql query. */ start: function(transId, is_query_tool, editor_title, script_type_url, - server_type, url_params + server_type, url_params, layout ) { var self = this; @@ -2087,6 +2103,7 @@ define('tools.querytool', [ self.gridView = new SQLEditorView({ el: self.container, handler: self, + layout: layout, }); self.transId = self.gridView.transId = transId; diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py index 7163192c4..786cab0a7 100644 --- a/web/regression/python_test_utils/test_utils.py +++ b/web/regression/python_test_utils/test_utils.py @@ -727,11 +727,13 @@ def reset_layout_db(user_id=None): if user_id is None: cur.execute( - 'DELETE FROM SETTING WHERE SETTING="Browser/Layout"' + 'DELETE FROM SETTING WHERE SETTING in ' + '("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")' ) else: cur.execute( - 'DELETE FROM SETTING WHERE SETTING="Browser/Layout"' + 'DELETE FROM SETTING WHERE SETTING in ' + '("Browser/Layout", "SQLEditor/Layout", "Debugger/Layout")' ' AND USER_ID=?', user_id ) conn.commit() diff --git a/web/yarn.lock b/web/yarn.lock index d6125dc8e..f970ec5f8 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -9015,9 +9015,9 @@ watchpack@^1.5.0: graceful-fs "^4.1.2" neo-async "^2.5.0" -"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#32b58375ac57c8b7c681a64fea226ce306dee33a": +"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d": version "2.2.4-dev" - resolved "git+https://github.com/EnterpriseDB/wcDocker/#32b58375ac57c8b7c681a64fea226ce306dee33a" + resolved "git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d" dependencies: FileSaver "^0.10.0" font-awesome "^4.7.0"