diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py index b2fd3131f..180807fb4 100644 --- a/web/pgadmin/browser/server_groups/servers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/__init__.py @@ -70,6 +70,13 @@ class ServerModule(sg.ServerGroupPluginModule): conn = manager.connection() connected = conn.connected() + status, in_recovery = conn.execute_scalar(""" + SELECT CASE WHEN usesuper + THEN pg_is_in_recovery() + ELSE FALSE + END as inrecovery + FROM pg_user WHERE usename=current_user""") + yield self.generate_browser_node( "%d" % (server.id), gid, @@ -82,7 +89,8 @@ class ServerModule(sg.ServerGroupPluginModule): server_type=manager.server_type if connected else "pg", version=manager.version, db=manager.db, - user=manager.user_info if connected else None + user=manager.user_info if connected else None, + in_recovery=in_recovery ) @property @@ -181,6 +189,8 @@ class ServerNode(PGChildNodeView): 'module.js': [{}, {}, {'get': 'module_js'}], 'reload': [{'get': 'reload_configuration'}], + 'restore_point': + [{'post': 'create_restore_point'}], 'connect': [{ 'get': 'connect_status', 'post': 'connect', 'delete': 'disconnect' }] @@ -781,5 +791,49 @@ class ServerNode(PGChildNodeView): return make_json_response(data={'status': False, 'result': gettext('Not connected to the server or the connection to the server has been closed.')}) + def create_restore_point(self, gid, sid): + """ + This method will creates named restore point + + Args: + gid: Server group ID + sid: Server ID + + Returns: + None + """ + try: + data = request.form + restore_point_name = data['value'] if data else None + from pgadmin.utils.driver import get_driver + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + conn = manager.connection() + + # Execute SQL to create named restore point + if conn.connected(): + if restore_point_name: + status, res = conn.execute_scalar( + "SELECT pg_create_restore_point('{0}');".format( + restore_point_name + ) + ) + if not status: + return internal_server_error( + errormsg=str(res) + ) + + return make_json_response( + data={ + 'status': 1, + 'result': gettext( + 'Named restore point created: {0}'.format( + restore_point_name)) + }) + + except Exception as e: + current_app.logger.error( + 'Named restore point creation failed ({0})'.format(str(e)) + ) + return internal_server_error(errormsg=str(e)) ServerNode.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js index da52f7487..5300055ee 100644 --- a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js +++ b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js @@ -23,7 +23,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { applies: ['object', 'context'], callback: 'show_obj_properties', category: 'create', priority: 1, label: '{{ _('Server...') }}', data: {action: 'create'}, icon: 'wcTabIcon icon-server' - }, { + },{ name: 'create_server', node: 'server', module: this, applies: ['object', 'context'], callback: 'show_obj_properties', category: 'create', priority: 3, label: '{{ _('Server...') }}', @@ -33,18 +33,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { applies: ['object', 'context'], callback: 'connect_server', category: 'connect', priority: 4, label: '{{ _('Connect Server...') }}', icon: 'fa fa-link', enable : 'is_not_connected' - }, - { + },{ name: 'disconnect_server', node: 'server', module: this, applies: ['object', 'context'], callback: 'disconnect_server', category: 'drop', priority: 5, label: '{{ _('Disconnect Server...') }}', icon: 'fa fa-chain-broken', enable : 'is_connected' - }, - { + },{ name: 'reload_configuration', node: 'server', module: this, applies: ['tools', 'context'], callback: 'reload_configuration', category: 'reload', priority: 6, label: '{{ _('Reload Configuration...') }}', icon: 'fa fa-repeat', enable : 'enable_reload_config' + },{ + name: 'restore_point', node: 'server', module: this, + applies: ['tools', 'context'], callback: 'restore_point', + category: 'restore', priority: 7, label: '{{ _('Add named restore point') }}', + icon: 'fa fa-anchor', enable : 'is_applicable' }]); pgBrowser.messages['PRIV_GRANTEE_NOT_SPECIFIED'] = @@ -59,7 +62,18 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { return (node && node.connected == true); }, enable_reload_config: function(node) { - if (node && node._type == "server" && node.connected == true) { + // Must be connected & is Super user + if (node && node._type == "server" && + node.connected && node.user.is_superuser) { + return true + } + return false; + }, + is_applicable: function(node) { + // Must be connected & super user & not in recovery mode + if (node && node._type == "server" && + node.connected && node.user.is_superuser + && node.in_recovery == false) { return true; } return false; @@ -199,6 +213,46 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { }); return false; + }, + /* Add restore point */ + restore_point: function(args) { + var input = args || {}; + obj = this, + t = pgBrowser.tree, + i = input.item || t.selected(), + d = i && i.length == 1 ? t.itemData(i) : undefined; + + if (!d) + return false; + + alertify.prompt('{{ _('Enter the name of the restore point') }}', '', + // We will execute this function when user clicks on the OK button + function(evt, value) { + // If user has provided a value, send it to the server + if(!_.isUndefined(value) && !_.isNull(value) && value !== '') { + $.ajax({ + url: obj.generate_url(i, 'restore_point', d, true), + method:'POST', + data:{ 'value': JSON.stringify(value) }, + success: function(res) { + alertify.success(res.data.result, 10); + }, + error: function(xhr, status, error) { + try { + var err = $.parseJSON(xhr.responseText); + if (err.success == 0) { + alertify.error(err.errormsg, 10); + } + } catch (e) {} + t.unload(i); + } + }); + } else { + alertify.error('{{ _('Please enter a valid name.') }}', 10); + } + } + ); + } }, model: pgAdmin.Browser.Node.Model.extend({