diff --git a/docs/en_US/images/server_general.png b/docs/en_US/images/server_general.png old mode 100755 new mode 100644 index c94a1afb3..c9c042356 Binary files a/docs/en_US/images/server_general.png and b/docs/en_US/images/server_general.png differ diff --git a/docs/en_US/server_dialog.rst b/docs/en_US/server_dialog.rst index 95df7bb9d..36cd2117a 100644 --- a/docs/en_US/server_dialog.rst +++ b/docs/en_US/server_dialog.rst @@ -16,6 +16,8 @@ Use the fields in the *General* tab to identify the server: * Use the *Name* field to add a descriptive name for the server; the name specified will be displayed in the *pgAdmin* tree control of the client. * Use the drop-down list box in the *Server group* field to specify the *pgAdmin* tree control parent node for the server. +* Use the color-picker in the *Background* field to specify the background color for the server. +* Use the color-picker in the *Foreground* field to specify the foreground color for the server. * Uncheck the checkbox next to *Connect now?* to instruct pgAdmin not to attempt a connection upon completion of the dialog. The default enables connection. * Provide a comment about the server in the *Comments* field. diff --git a/libraries.txt b/libraries.txt index f33f26cf0..5d10cc00f 100644 --- a/libraries.txt +++ b/libraries.txt @@ -36,3 +36,4 @@ jQuery-UI 1.11.3 MIT https://jqueryui.com/ BigNumber 3.0.1 MIT http://mikemcl.github.io/bignumber.js Source Code Pro 1.1 SIL OFL https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700 Open Sans 2.0 AL https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,700 +Spectrum 1.8 MIT https://bgrins.github.io/spectrum/ diff --git a/web/migrations/versions/02b9dccdcfcb_.py b/web/migrations/versions/02b9dccdcfcb_.py new file mode 100644 index 000000000..4b8e98e18 --- /dev/null +++ b/web/migrations/versions/02b9dccdcfcb_.py @@ -0,0 +1,35 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2017, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +""" +Adding new columns to store background & foreground (Feature: RM#1383) + +Revision ID: 02b9dccdcfcb +Revises: ef590e979b0d +Create Date: 2017-11-14 19:09:04.674575 + +""" +from pgadmin.model import db + +# revision identifiers, used by Alembic. +revision = '02b9dccdcfcb' +down_revision = 'ef590e979b0d' +branch_labels = None +depends_on = None + + +def upgrade(): + db.engine.execute( + 'ALTER TABLE server ADD COLUMN bgcolor TEXT(10)' + ) + db.engine.execute( + 'ALTER TABLE server ADD COLUMN fgcolor TEXT(10)' + ) + +def downgrade(): + pass diff --git a/web/package.json b/web/package.json index fc6f56d64..54b8445ce 100644 --- a/web/package.json +++ b/web/package.json @@ -78,6 +78,7 @@ "shim-loader": "^1.0.1", "slickgrid": "git+https://github.com/6pac/SlickGrid.git#2.3.7", "snapsvg": "^0.5.1", + "spectrum-colorpicker": "^1.8.0", "underscore": "^1.8.3", "underscore.string": "^3.3.4", "watchify": "~3.9.0", diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py index 25ec28afc..91b27f7c4 100644 --- a/web/pgadmin/browser/server_groups/servers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/__init__.py @@ -43,7 +43,6 @@ def has_any(data, keys): return False - def recovery_state(connection, postgres_version): recovery_check_sql = render_template("connect/sql/#{0}#/check_recovery.sql".format(postgres_version)) @@ -56,6 +55,38 @@ def recovery_state(connection, postgres_version): wal_paused = None return in_recovery, wal_paused +def server_icon_and_background(is_connected, manager, server): + """ + + Args: + is_connected: Flag to check if server is connected + manager: Connection manager + server: Sever object + + Returns: + Server Icon CSS class + """ + server_background_color = '' + if server and server.bgcolor: + server_background_color = ' {0}'.format( + server.bgcolor + ) + # If user has set font color also + if server.fgcolor: + server_background_color = '{0} {1}'.format( + server_background_color, + server.fgcolor + ) + + if is_connected: + return 'icon-{0}{1}'.format( + manager.server_type, server_background_color + ) + else: + return 'icon-server-not-connected{0}'.format( + server_background_color + ) + class ServerModule(sg.ServerGroupPluginModule): NODE_TYPE = "server" @@ -87,14 +118,14 @@ class ServerModule(sg.ServerGroupPluginModule): connected = conn.connected() in_recovery = None wal_paused = None + if connected: in_recovery, wal_paused = recovery_state(conn, manager.version) yield self.generate_browser_node( "%d" % (server.id), gid, server.name, - "icon-server-not-connected" if not connected else - "icon-{0}".format(manager.server_type), + server_icon_and_background(connected, manager, server), True, self.NODE_TYPE, connected=connected, @@ -305,8 +336,7 @@ class ServerNode(PGChildNodeView): "%d" % (server.id), gid, server.name, - "icon-server-not-connected" if not connected else - "icon-{0}".format(manager.server_type), + server_icon_and_background(connected, manager, server), True, self.node_type, connected=connected, @@ -359,8 +389,7 @@ class ServerNode(PGChildNodeView): "%d" % (server.id), gid, server.name, - "icon-server-not-connected" if not connected else - "icon-{0}".format(manager.server_type), + server_icon_and_background(connected, manager, server), True, self.node_type, connected=connected, @@ -435,7 +464,9 @@ class ServerNode(PGChildNodeView): 'sslkey': 'sslkey', 'sslrootcert': 'sslrootcert', 'sslcrl': 'sslcrl', - 'sslcompression': 'sslcompression' + 'sslcompression': 'sslcompression', + 'bgcolor': 'bgcolor', + 'fgcolor': 'fgcolor' } disp_lbl = { @@ -515,8 +546,7 @@ class ServerNode(PGChildNodeView): node=self.blueprint.generate_browser_node( "%d" % (server.id), server.servergroup_id, server.name, - "icon-server-not-connected" if not connected else - "icon-{0}".format(manager.server_type), + server_icon_and_background(connected, manager, server), True, self.node_type, connected=False, @@ -610,6 +640,8 @@ class ServerNode(PGChildNodeView): 'version': manager.ver, 'sslmode': server.ssl_mode, 'server_type': manager.server_type if connected else 'pg', + 'bgcolor': server.bgcolor, + 'fgcolor': server.fgcolor, 'db_res': server.db_res.split(',') if server.db_res else None, 'passfile': server.passfile if server.passfile else None, 'sslcert': server.sslcert if is_ssl else None, @@ -680,7 +712,12 @@ class ServerNode(PGChildNodeView): sslkey=data['sslkey'] if is_ssl else None, sslrootcert=data['sslrootcert'] if is_ssl else None, sslcrl=data['sslcrl'] if is_ssl else None, - sslcompression=1 if is_ssl and data['sslcompression'] else 0 + sslcompression=1 if is_ssl and data['sslcompression'] else 0, + bgcolor=data['bgcolor'] if u'bgcolor' in data + else None, + fgcolor = data['fgcolor'] if u'fgcolor' in data + else None + ) db.session.add(server) db.session.commit() @@ -735,7 +772,7 @@ class ServerNode(PGChildNodeView): node=self.blueprint.generate_browser_node( "%d" % server.id, server.servergroup_id, server.name, - 'icon-{0}'.format(manager.server_type) if manager and manager.server_type else "icon-pg", + server_icon_and_background(connected, manager, server), True, self.node_type, user=user, @@ -968,9 +1005,7 @@ class ServerNode(PGChildNodeView): success=1, info=gettext("Server connected."), data={ - 'icon': 'icon-{0}'.format( - manager.server_type - ), + 'icon': server_icon_and_background(True, manager, server), 'connected': True, 'server_type': manager.server_type, 'type': manager.server_type, @@ -1001,7 +1036,7 @@ class ServerNode(PGChildNodeView): success=1, info=gettext("Server disconnected."), data={ - 'icon': 'icon-server-not-connected', + 'icon': server_icon_and_background(False, manager, server), 'connected': False } ) diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js index 7da749762..f0feba1eb 100644 --- a/web/pgadmin/browser/server_groups/servers/static/js/server.js +++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js @@ -651,6 +651,13 @@ define('pgadmin.node.server', [ },{ id: 'version', label: gettext('Version'), type: 'text', group: null, mode: ['properties'], visible: 'isConnected' + },{ + id: 'bgcolor', label: gettext('Background'), type: 'color', + group: null, mode: ['edit', 'create'], disabled: 'isfgColorSet', + deps: ['fgcolor'] + },{ + id: 'fgcolor', label: gettext('Foreground'), type: 'color', + group: null, mode: ['edit', 'create'], disabled: 'isConnected', },{ id: 'connect_now', controlLabel: gettext('Connect now?'), type: 'checkbox', group: null, mode: ['create'] @@ -881,6 +888,23 @@ define('pgadmin.node.server', [ isConnected: function(model) { return model.get('connected'); }, + isfgColorSet: function(model) { + var bgcolor = model.get('bgcolor'), + fgcolor = model.get('fgcolor'); + + if(model.get('connected')) { + return true; + } + // If fgcolor is set and bgcolor is not set then force bgcolor + // to set as white + if(_.isUndefined(bgcolor) || _.isNull(bgcolor) || !bgcolor) { + if(fgcolor) { + model.set('bgcolor', '#ffffff'); + } + } + + return false; + }, isSSL: function(model) { var ssl_mode = model.get('sslmode'); // If server is not connected and have required SSL option diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js index ad4f84b23..4aa5b8ba1 100644 --- a/web/pgadmin/browser/static/js/browser.js +++ b/web/pgadmin/browser/static/js/browser.js @@ -1509,7 +1509,15 @@ define( } } - if (_old._pid != _new._pid || _old._label != _new._label) { + // If server icon/background changes then also we need to re-create it + if(_old._type == 'server' && _new._type == 'server' && + ( _old._pid != _new._pid || + _old._label != _new._label || + _old.icon != _new.icon ) + ) { + ctx.op = 'RECREATE'; + traversePath(); + } else if (_old._pid != _new._pid || _old._label != _new._label) { ctx.op = 'RECREATE'; traversePath(); } else { diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index 2beadf969..a9c2b1863 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -732,6 +732,48 @@ define( // Here call data grid method to render query tool pgAdmin.DataGrid.show_query_tool('', i); }, + + // Logic to change the server background colour + // There is no way of applying CSS to parent element so we have to + // do it via JS code only + change_server_background: function(item, data) { + if (!item || !data) + return; + + // Go further only if node type is a Server + if (data && data._type && data._type == 'server') { + var element = $(item).find('span.aciTreeItem').first() || null, + // First element will be icon and second will be colour code + bgcolor = data.icon.split(" ")[1] || null, + fgcolor = data.icon.split(" ")[2] || ''; + + if(bgcolor) { + // li tag for the current branch + var first_level_element = element.parents()[3] || null, + dynamic_class = 'pga_server_' + data._id + '_bgcolor', + style_tag; + + // Prepare dynamic style tag + style_tag = ""; + + // Prepare dynamic style tag using template + $('#' + dynamic_class).remove(); + $(style_tag).appendTo("head"); + + if(first_level_element) + $(first_level_element).addClass(dynamic_class); + } + } + }, + added: function(item, data, browser) { var b = browser || pgBrowser, t = b.tree, @@ -757,6 +799,8 @@ define( } ); } + + pgBrowser.Node.callbacks.change_server_background(item, data); }, // Callback called - when a node is selected in browser tree. selected: function(item, data, browser) { diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py index a3cf1750c..ee68c150a 100644 --- a/web/pgadmin/model/__init__.py +++ b/web/pgadmin/model/__init__.py @@ -138,6 +138,8 @@ class Server(db.Model): db.CheckConstraint( 'sslcompression >= 0 AND sslcompression <= 1' ), nullable=False) + bgcolor = db.Column(db.Text(10), nullable=True) + fgcolor = db.Column(db.Text(10), nullable=True) class ModulePreference(db.Model): diff --git a/web/pgadmin/static/css/aci_tree.overrides.css b/web/pgadmin/static/css/aci_tree.overrides.css index 913957b59..921d711b0 100644 --- a/web/pgadmin/static/css/aci_tree.overrides.css +++ b/web/pgadmin/static/css/aci_tree.overrides.css @@ -9,13 +9,25 @@ border-color: #84acdd; } +.aciTree .aciTreeLine.aciTreeHover .aciTreeText { + color: black; +} + /* Selected element and in focus*/ .aciTree.aciTreeFocus .aciTreeFocus > .aciTreeLine .aciTreeItem { background-color: #e7f2ff; } +.aciTree.aciTreeFocus .aciTreeFocus > .aciTreeLine .aciTreeText { + color: black; +} + /* Selected element but not in focus */ .aciTree .aciTreeSelected > .aciTreeLine .aciTreeItem { background-color: #e7f2ff; border-color: #84acdd; } + +.aciTree .aciTreeItem { + white-space: nowrap !important; +} diff --git a/web/pgadmin/static/css/bootstrap.overrides.css b/web/pgadmin/static/css/bootstrap.overrides.css index 73b4fc700..8790bba23 100755 --- a/web/pgadmin/static/css/bootstrap.overrides.css +++ b/web/pgadmin/static/css/bootstrap.overrides.css @@ -61,11 +61,6 @@ iframe { padding-left: 0; } -/* Prevent tree items wrapping */ -.aciTree .aciTreeItem { - white-space: nowrap !important; -} - /* Disabled menu items */ .mnu-disabled { color: #999999 !important; @@ -1346,3 +1341,61 @@ body { .alert-dismissable, .alert-dismissible { padding-right: 35px !important; } + +/* Override CSS for Colour Picker to match it with pgAdmin4 style */ +.sp-replacer { + background: #fff; + border: solid 1px #ccc; + border-radius: 3px; +} + +.sp-replacer:hover { + border-color: #ccc; +} + +.sp-replacer.sp-active { + border-color: #66afe9; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +.sp-replacer.sp-disabled { + background-color: #eee; +} + +.sp-container { + background-color: #fff; + border: solid 1px #ccc; + border-radius: 3px !important; +} + +.sp-palette-button-container button, +.sp-palette-button-container button:hover { + padding: 5px; + background-color: #2c76b4; + background-image: none; + font-family: 'Open Sans'; + font-size: 12px; + color: #fff; + text-shadow: none; + -webkit-font-smoothing: auto; +} + +.sp-container button:active { + border: 1px solid #84acdd; + border-bottom: none; + -webkit-box-shadow: inset 0 0 5px 2px #84acdd, 0 1px 0 0 #84acdd; + -moz-box-shadow: inset 0 0 5px 2px #84acdd, 0 1px 0 0 #84acdd; + -ms-box-shadow: inset 0 0 5px 2px #84acdd, 0 1px 0 0 #84acdd; + -o-box-shadow: inset 0 0 5px 2px #84acdd, 0 1px 0 0 #84acdd; + box-shadow: inset 0 0 5px 2px #84acdd, 0 1px 0 0 #84acdd; +} + +.sp-dd { + font-size: 8px; + color: #888888; +} diff --git a/web/pgadmin/static/css/style.css b/web/pgadmin/static/css/style.css index ead60bfdb..ed88eabca 100644 --- a/web/pgadmin/static/css/style.css +++ b/web/pgadmin/static/css/style.css @@ -13,6 +13,7 @@ @import '~jquery-contextmenu/dist/jquery.contextMenu.css'; @import '~webcabin-docker/Build/wcDocker.css'; @import '~acitree/css/aciTree.css'; +@import '~spectrum-colorpicker/spectrum.css'; @import '~codemirror/lib/codemirror.css'; @import '~codemirror/addon/dialog/dialog.css'; diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 7d446fe74..bfc58efd0 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -4,8 +4,8 @@ if (typeof define === 'function' && define.amd) { define([ 'sources/gettext', 'underscore', 'underscore.string', 'jquery', - 'backbone', 'backform', 'backgrid', 'codemirror', 'pgadmin.backgrid', - 'select2' + 'backbone', 'backform', 'backgrid', 'codemirror', 'spectrum', + 'pgadmin.backgrid', 'select2' ], function(gettext, _, S, $, Backbone, Backform, Backgrid, CodeMirror) { // Export global even in AMD case in case this script is loaded with @@ -68,7 +68,8 @@ 'uniqueColCollection': ['unique-col-collection', 'unique-col-collection', 'string'], 'switch' : 'switch', 'select2': 'select2', - 'note': 'note' + 'note': 'note', + 'color': 'color' }; var getMappedControl = Backform.getMappedControl = function(type, mode) { @@ -2367,5 +2368,95 @@ } }); + + // Color Picker control + var ColorControl = Backform.ColorControl = Backform.InputControl.extend({ + defaults: { + label: "", + extraClasses: [], + helpMessage: null, + showButtons: false, + showPalette: true, + allowEmpty: true, + colorFormat: "hex", + defaultColor: "" + }, + template: _.template([ + '', + '