diff --git a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx index e7aa57907..e29786308 100644 --- a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx +++ b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx @@ -356,6 +356,12 @@ export default function PreferencesComponent({ ...props }) { } + function selectChildNode(item, prefTreeInit) { + if (item.isExpanded && item._children && item._children.length > 0 && prefTreeInit.current && event.code !== 'ArrowUp') { + pgAdmin.Browser.ptree.tree.setActiveFile(item._children[0], true); + } + } + useEffect(() => { let initTreeTimeout = null; let firstElement = null; @@ -379,9 +385,7 @@ export default function PreferencesComponent({ ...props }) { }, 10); } else { - if (item.isExpanded && item._children && item._children.length > 0 && prefTreeInit.current && event.code !== 'ArrowUp') { - pgAdmin.Browser.ptree.tree.setActiveFile(item._children[0], true); - } + selectChildNode(item, prefTreeInit); } }); @@ -391,21 +395,23 @@ export default function PreferencesComponent({ ...props }) { }); // Listen added preferences tree node event to expand the newly added node on tree load. - pgAdmin.Browser.Events.on('preferences:tree:added', (event, item) => { - if (item._parent._fileName == firstTreeElement.current && item._parent.isExpanded && !prefTreeInit.current) { - pgAdmin.Browser.ptree.tree.setActiveFile(item._parent._children[0], true); - } - else if (item.type == FileType.Directory) { - // Check the if newely added node is Directoy and call toggle to expand the node. - pgAdmin.Browser.ptree.tree.toggleDirectory(item); - } - }); + pgAdmin.Browser.Events.on('preferences:tree:added', addPrefTreeNode); /* Clear the initTreeTimeout timeout if unmounted */ return () => { clearTimeout(initTreeTimeout); }; }, []); + function addPrefTreeNode(event, item) { + if (item._parent._fileName == firstTreeElement.current && item._parent.isExpanded && !prefTreeInit.current) { + pgAdmin.Browser.ptree.tree.setActiveFile(item._parent._children[0], true); + } + else if (item.type == FileType.Directory) { + // Check the if newely added node is Directoy and call toggle to expand the node. + pgAdmin.Browser.ptree.tree.toggleDirectory(item); + } + } + function getControlMappedForType(type) { switch (type) { case 'text': diff --git a/web/pgadmin/static/js/helpers/ModalProvider.jsx b/web/pgadmin/static/js/helpers/ModalProvider.jsx index be18ded0d..b6cba5a0b 100644 --- a/web/pgadmin/static/js/helpers/ModalProvider.jsx +++ b/web/pgadmin/static/js/helpers/ModalProvider.jsx @@ -155,14 +155,19 @@ const dialogStyle = makeStyles((theme) => ({ } })); + +function checkIsResizable(props) { + return props.isresizeable == 'true' ? true : false; +} + +function setEnableResizing(props, resizeable) { + return props.isfullscreen == 'true' ? false : resizeable; +} + function PaperComponent({minHeight, minWidth, ...props}) { let classes = dialogStyle(); let [dialogPosition, setDialogPosition] = useState(null); - let resizeable = props.isresizeable == 'true' ? true : false; - - const setEnableResizing = () => { - return props.isfullscreen == 'true' ? false : resizeable; - }; + let resizeable = checkIsResizable(props); const setConditionalPosition = () => { return props.isfullscreen == 'true' ? { x: 0, y: 0 } : dialogPosition && { x: dialogPosition.x, y: dialogPosition.y }; @@ -187,7 +192,7 @@ function PaperComponent({minHeight, minWidth, ...props}) { // {...(props.width && { minWidth: MIN_WIDTH })} // {...(props.height && { minHeight: MIN_HEIGHT })} bounds="window" - enableResizing={setEnableResizing()} + enableResizing={setEnableResizing(props, resizeable)} position={setConditionalPosition()} onDragStop={(e, position) => { if (props.isfullscreen !== 'true') { diff --git a/web/pgadmin/tools/grant_wizard/__init__.py b/web/pgadmin/tools/grant_wizard/__init__.py index 7921655bd..bbe57918d 100644 --- a/web/pgadmin/tools/grant_wizard/__init__.py +++ b/web/pgadmin/tools/grant_wizard/__init__.py @@ -245,6 +245,22 @@ def _get_rows_for_type(conn, ntype, server_prop, node_id): return status, res +def get_node_sql_with_type(node_id, node_type, server_prop, + get_schema_sql_url, show_sysobj): + if node_type == 'database': + sql = render_template("/".join( + [server_prop['template_path'], get_schema_sql_url]), + show_sysobj=show_sysobj) + ntype = 'schema' + else: + sql = render_template("/".join( + [server_prop['template_path'], get_schema_sql_url]), + show_sysobj=show_sysobj, nspid=node_id) + ntype = node_type + + return sql, ntype + + @blueprint.route( '/////', methods=['GET'], endpoint='objects' @@ -269,16 +285,9 @@ def properties(sid, did, node_id, node_type): conn = manager.connection(did=did) show_sysobj = blueprint.show_system_objects().get() - if node_type == 'database': - sql = render_template("/".join( - [server_prop['template_path'], get_schema_sql_url]), - show_sysobj=show_sysobj) - ntype = 'schema' - else: - sql = render_template("/".join( - [server_prop['template_path'], get_schema_sql_url]), - show_sysobj=show_sysobj, nspid=node_id) - ntype = node_type + + sql, ntype = get_node_sql_with_type(node_id, node_type, server_prop, + get_schema_sql_url, show_sysobj) status, res = conn.execute_dict(sql) @@ -351,6 +360,17 @@ def properties(sid, did, node_id, node_type): ) +def get_req_data(): + return request.form if request.form else json.loads(request.data.decode()) + + +def set_priv_for_package(server_prop, data, acls): + if server_prop['server_type'] == 'ppas': + data['priv']['package'] = parse_priv_to_db( + data['acl'], + acls['package']['acl']) + + @blueprint.route( '/sql///', methods=['POST'], endpoint='modified_sql' @@ -362,7 +382,7 @@ def msql(sid, did): This function will return modified SQL """ server_prop = server_info - data = request.form if request.form else json.loads(request.data.decode()) + data = get_req_data() # Form db connection manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) conn = manager.connection(did=did) @@ -398,10 +418,7 @@ def msql(sid, did): acls['foreign_table']['acl']) # Logic for setting privileges only for ppas - if server_prop['server_type'] == 'ppas': - data['priv']['package'] = parse_priv_to_db( - data['acl'], - acls['package']['acl']) + set_priv_for_package(server_prop, data, acls) # Pass database objects and get SQL for privileges sql_data = '' @@ -466,6 +483,29 @@ def msql(sid, did): ) +def parse_priv(data, acls, server_prop): + if 'acl' in data: + # Get function acls + data['priv']['function'] = parse_priv_to_db( + data['acl'], + acls['function']['acl']) + + data['priv']['sequence'] = parse_priv_to_db( + data['acl'], + acls['sequence']['acl']) + + data['priv']['table'] = parse_priv_to_db( + data['acl'], + acls['table']['acl']) + + data['priv']['foreign_table'] = parse_priv_to_db( + data['acl'], + acls['foreign_table']['acl']) + + # Logic for setting privileges only for ppas + set_priv_for_package(server_prop, data, acls) + + @blueprint.route( '///', methods=['POST'], endpoint='apply' ) @@ -477,7 +517,7 @@ def save(sid, did): Database Objects """ server_prop = server_info - data = request.form if request.form else json.loads(request.data.decode()) + data = get_req_data() # Form db connection and we use conn to execute sql manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) @@ -496,30 +536,7 @@ def save(sid, did): # Parse privileges data['priv'] = {} - if 'acl' in data: - # Get function acls - data['priv']['function'] = parse_priv_to_db( - data['acl'], - acls['function']['acl']) - - data['priv']['sequence'] = parse_priv_to_db( - data['acl'], - acls['sequence']['acl']) - - data['priv']['table'] = parse_priv_to_db( - data['acl'], - acls['table']['acl']) - - data['priv']['foreign_table'] = parse_priv_to_db( - data['acl'], - acls['foreign_table']['acl']) - - # Logic for setting privileges only for ppas - if server_prop['server_type'] == 'ppas': - data['priv']['package'] = parse_priv_to_db( - data['acl'], - acls['package']['acl']) - + parse_priv(data, acls, server_prop) # Pass database objects and get SQL for privileges # Pass database objects and get SQL for privileges sql_data = '' diff --git a/web/pgadmin/tools/psql/__init__.py b/web/pgadmin/tools/psql/__init__.py index ef489291e..e8cd399b6 100644 --- a/web/pgadmin/tools/psql/__init__.py +++ b/web/pgadmin/tools/psql/__init__.py @@ -8,14 +8,13 @@ ########################################################################## import os -import re import select import struct import config from sys import platform as _platform import eventlet.green.subprocess as subprocess from config import PG_DEFAULT_DRIVER -from flask import Response, url_for, request +from flask import Response, request from flask import render_template, copy_current_request_context, \ current_app as app from flask_babel import gettext @@ -215,6 +214,51 @@ def read_stdout(process, sid, max_read_bytes, win_emit_output=True): sio.sleep(0) +def windows_platform(connection_data, sid, max_read_bytes): + os.environ['PYWINPTY_BACKEND'] = '1' + process = PtyProcess.spawn('cmd.exe') + + process.write(r'"{0}" "{1}" 2>>&1'.format(connection_data[0], + connection_data[1])) + process.write("\r\n") + app.config['sessions'][request.sid] = process + pdata[request.sid] = process + set_term_size(process, 50, 50) + + while True: + read_stdout(process, sid, max_read_bytes, + win_emit_output=True) + + +def non_windows_platform(parent, p, fd, data, max_read_bytes, sid): + while p and p.poll() is None: + if request.sid in app.config['sessions']: + # This code is added to make this unit testable. + if "is_test" not in data: + sio.sleep(0.01) + else: + data['count'] += 1 + if data['count'] == 5: + break + + timeout = 0 + # module provides access to platform-specific I/O + # monitoring functions + (data_ready, _, _) = select.select([parent, fd], [], [], + timeout) + + read_terminal_data(parent, data_ready, max_read_bytes, sid) + + +def pty_handel_io(connection_data, data, sid): + max_read_bytes = 1024 * 20 + if _platform == 'win32': + windows_platform(connection_data, sid, max_read_bytes) + else: + p, parent, fd = create_pty_terminal(connection_data) + non_windows_platform(parent, p, fd, data, max_read_bytes, sid) + + @sio.on('start_process', namespace='/pty') def start_process(data): """ @@ -224,45 +268,7 @@ def start_process(data): """ @copy_current_request_context def read_and_forward_pty_output(sid, data): - - max_read_bytes = 1024 * 20 - import time - if _platform == 'win32': - - os.environ['PYWINPTY_BACKEND'] = '1' - process = PtyProcess.spawn('cmd.exe') - - process.write(r'"{0}" "{1}" 2>>&1'.format(connection_data[0], - connection_data[1])) - process.write("\r\n") - app.config['sessions'][request.sid] = process - pdata[request.sid] = process - set_term_size(process, 50, 50) - - while True: - read_stdout(process, sid, max_read_bytes, - win_emit_output=True) - else: - - p, parent, fd = create_pty_terminal(connection_data) - - while p and p.poll() is None: - if request.sid in app.config['sessions']: - # This code is added to make this unit testable. - if "is_test" not in data: - sio.sleep(0.01) - else: - data['count'] += 1 - if data['count'] == 5: - break - - timeout = 0 - # module provides access to platform-specific I/O - # monitoring functions - (data_ready, _, _) = select.select([parent, fd], [], [], - timeout) - - read_terminal_data(parent, data_ready, max_read_bytes, sid) + pty_handel_io(connection_data, data, sid) # Check user is authenticated and PSQL is enabled in config. if current_user.is_authenticated and config.ENABLE_PSQL: @@ -555,6 +561,13 @@ def disconnect_socket(): del app.config['sessions'][request.sid] +def get_connection_status(conn): + if conn.connected(): + return True + + return False + + def _get_database(sid, did): """ This method is used to get database based on sid, did. @@ -564,10 +577,9 @@ def _get_database(sid, did): manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(int(sid)) conn = manager.connection() db_name = None - if conn.connected(): - is_connected = True - else: - is_connected = False + + is_connected = get_connection_status(conn) + if is_connected: if conn.manager and conn.manager.db_info \ diff --git a/web/pgadmin/tools/psql/static/js/psql_module.js b/web/pgadmin/tools/psql/static/js/psql_module.js index f1c06786a..1683a95d3 100644 --- a/web/pgadmin/tools/psql/static/js/psql_module.js +++ b/web/pgadmin/tools/psql/static/js/psql_module.js @@ -17,6 +17,7 @@ import {getRandomInt, hasBinariesConfiguration, registerDetachEvent} from 'sourc import {retrieveAncestorOfTypeServer} from 'sources/tree/tree_utils'; import pgWindow from 'sources/window'; import Notify from '../../../../static/js/helpers/Notifier'; +import { copyToClipboard } from '../../../../static/js/clipboard'; import {generateTitle, refresh_db_node} from 'tools/sqleditor/static/js/sqleditor_title'; @@ -328,7 +329,14 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser) term.attachCustomKeyEventHandler(e => { e.stopPropagation(); if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) { - document.execCommand('copy'); + let selected_text = term.getSelection(); + navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) { + if(result.state === 'granted' || result.state === 'prompt') { + copyToClipboard(selected_text); + } else{ + Notify.alert(gettext('Clipboard write permission required'), gettext('To copy data from PSQL terminal, Clipboard write permission required.')); + } + }); } return !(e.ctrlKey && platform == 'win32');