diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index 86259d32d..1051c6b5d 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -509,7 +509,7 @@ define('pgadmin.browser.node', [ w: (!_.isUndefined(width) && !_.isNull(width)) ? width : (screen.width < 700 ? screen.width * 0.95 : screen.width * 0.5), h: (!_.isUndefined(height) && !_.isNull(height)) ? height : - (screen.height < 500 ? screen.height * 0.95 : screen.height * 0.35), + (screen.height < 500 ? screen.height * 0.95 : screen.height * 0.4), x: (screen.width < 700 ? '2%' : '25%'), y: (screen.height < 500 ? '2%' : '25%'), } diff --git a/web/pgadmin/browser/static/js/utility_view.jsx b/web/pgadmin/browser/static/js/utility_view.jsx index bfe3dfed8..b9c92111d 100644 --- a/web/pgadmin/browser/static/js/utility_view.jsx +++ b/web/pgadmin/browser/static/js/utility_view.jsx @@ -26,6 +26,9 @@ export function getUtilityView(schema, treeNodeInfo, actionType, formType, conta }; const confirmOnReset = pgAdmin.Browser.get_preferences_for_module('browser').confirm_on_properties_close; + /* button icons */ + const saveBtnIcon = extraData.save_btn_icon; + /* on save button callback, promise required */ const onSaveClick = (isNew, data)=>new Promise((resolve, reject)=>{ return api({ @@ -90,6 +93,7 @@ export function getUtilityView(schema, treeNodeInfo, actionType, formType, conta schema={_schema} viewHelperProps={viewHelperProps} customSaveBtnName={saveBtnName} + customSaveBtnIconType={saveBtnIcon} onSave={onSaveClick} onClose={()=>containerPanel.close()} onHelp={onHelp} diff --git a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py index a6490c628..75d67f09e 100644 --- a/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/pgadmin/feature_tests/pg_utilities_backup_restore_test.py @@ -240,16 +240,24 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest): self.page.fill_input_by_field_name( NavMenuLocators.restore_file_name_txt_box_name, - "test_backup", loose_focus=True) + "test_backup", input_keys=True, loose_focus=True) + # Click on the Restore button restore_btn = self.page.find_by_xpath( NavMenuLocators.restore_button_xpath) - restore_btn.click() - - self.page.wait_for_element_to_disappear( - lambda driver: driver.find_element( - By.CSS_SELECTOR, - NavMenuLocators.restore_file_name_txt_box_name)) + click = True + retry = 3 + while click and retry > 0: + try: + restore_btn.click() + if self.page.wait_for_element_to_disappear( + lambda driver: driver.find_element( + By.NAME, + NavMenuLocators.restore_file_name_txt_box_name)): + click = False + except Exception: + retry -= 1 + pass def _check_escaped_characters(self, source_code, string_to_find, source): # For XSS we need to search against element's html code diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx index a8c030260..64feeb27a 100644 --- a/web/pgadmin/static/js/SchemaView/index.jsx +++ b/web/pgadmin/static/js/SchemaView/index.jsx @@ -12,6 +12,7 @@ import { Box, makeStyles } from '@material-ui/core'; import {Accordion, AccordionSummary, AccordionDetails} from '@material-ui/core'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import SaveIcon from '@material-ui/icons/Save'; +import PublishIcon from '@material-ui/icons/Publish'; import SettingsBackupRestoreIcon from '@material-ui/icons/SettingsBackupRestore'; import CloseIcon from '@material-ui/icons/Close'; import InfoIcon from '@material-ui/icons/InfoRounded'; @@ -683,6 +684,15 @@ function SchemaDialogView({ formErr: formErr, }), [formResetKey, formErr]); + const getButtonIcon = () => { + if(props.customSaveBtnIconType == 'upload') { + return ; + } + return ; + }; + + let ButtonIcon = getButtonIcon(); + /* I am Groot */ return ( @@ -710,7 +720,7 @@ function SchemaDialogView({ } disabled={!dirty || saving} className={classes.buttonMargin}> {gettext('Reset')} - } disabled={!dirty || saving || Boolean(formErr.name) || !formReady}> + {props.customSaveBtnName ? gettext(props.customSaveBtnName) : gettext('Save')} @@ -745,6 +755,7 @@ SchemaDialogView.propTypes = { showFooter: PropTypes.bool, resetKey: PropTypes.any, customSaveBtnName: PropTypes.string, + customSaveBtnIconType: PropTypes.string, }; const usePropsStyles = makeStyles((theme)=>({ diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js index bd0744eb5..3e51a3dda 100644 --- a/web/pgadmin/tools/backup/static/js/backup.js +++ b/web/pgadmin/tools/backup/static/js/backup.js @@ -142,7 +142,7 @@ define([ }, startBackupGlobal: function(action, treeItem) { pgBrowser.Node.registerUtilityPanel(); - var panel = pgBrowser.Node.addUtilityPanel(); + var panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md); var tree = pgBrowser.tree, i = treeItem || tree.selected(), data = i ? tree.itemData(i) : undefined, @@ -159,7 +159,7 @@ define([ }, startBackupServer: function(action, treeItem) { pgBrowser.Node.registerUtilityPanel(); - var panel = pgBrowser.Node.addUtilityPanel(); + var panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md); var tree = pgBrowser.tree, i = treeItem || tree.selected(), data = i ? tree.itemData(i) : undefined, @@ -235,7 +235,7 @@ define([ } pgBrowser.Node.registerUtilityPanel(); - var panel = pgBrowser.Node.addUtilityPanel(), + var panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md, pgBrowser.stdH.lg), j = panel.$container.find('.obj_properties').first(); var schema = that.getUISchema(treeItem, 'backup_objects'); diff --git a/web/pgadmin/tools/backup/static/js/backup.ui.js b/web/pgadmin/tools/backup/static/js/backup.ui.js index 5ea2f865b..58d9b5316 100644 --- a/web/pgadmin/tools/backup/static/js/backup.ui.js +++ b/web/pgadmin/tools/backup/static/js/backup.ui.js @@ -515,7 +515,7 @@ export default class BackupSchema extends BaseUISchema { }, { type: 'nested-fieldset', label: gettext('Sections'), - group: gettext('Dump options'), + group: gettext('Data/Objects'), schema:new getSectionSchema(), visible: function() { if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') @@ -525,27 +525,27 @@ export default class BackupSchema extends BaseUISchema { }, { type: 'nested-fieldset', label: gettext('Type of objects'), - group: gettext('Dump options'), + group: gettext('Data/Objects'), schema: obj.getTypeObjSchema() }, { type: 'nested-fieldset', label: gettext('Do not save'), - group: gettext('Dump options'), + group: gettext('Data/Objects'), schema: obj.getSaveOptSchema(), }, { type: 'nested-fieldset', label: gettext('Queries'), - group: gettext('Dump options'), + group: gettext('Options'), schema: obj.getQueryOptionSchema(), }, { type: 'nested-fieldset', label: gettext('Disable'), - group: gettext('Dump options'), + group: gettext('Options'), schema: obj.getDisabledOptionSchema(), }, { type: 'nested-fieldset', label: gettext('Miscellaneous'), - group: gettext('Dump options'), + group: gettext('Options'), schema: obj.getMiscellaneousSchema(), }]; } diff --git a/web/pgadmin/tools/restore/static/js/restore.js b/web/pgadmin/tools/restore/static/js/restore.js index 76f00d55e..efa2e04fe 100644 --- a/web/pgadmin/tools/restore/static/js/restore.js +++ b/web/pgadmin/tools/restore/static/js/restore.js @@ -80,7 +80,7 @@ define('tools.restore', [ ()=>getRestoreSectionSchema({selectedNodeType: itemNodeData._type}), ()=>getRestoreTypeObjSchema({selectedNodeType: itemNodeData._type}), ()=>getRestoreSaveOptSchema({nodeInfo: treeNodeInfo}), - ()=>getRestoreQueryOptionSchema({nodeInfo: treeNodeInfo}), + ()=>getRestoreQueryOptionSchema({selectedNodeType: itemNodeData._type, nodeInfo: treeNodeInfo}), ()=>getRestoreDisableOptionSchema({nodeInfo: treeNodeInfo}), ()=>getRestoreMiscellaneousSchema({nodeInfo: treeNodeInfo}), { @@ -115,6 +115,7 @@ define('tools.restore', [ if('function' in treeInfo) { extraData['functions'] = [nodeData._label]; } + extraData['save_btn_icon'] = 'upload'; return extraData; }, url_for_utility_exists: function(id){ @@ -146,7 +147,7 @@ define('tools.restore', [ return; } pgBrowser.Node.registerUtilityPanel(); - var panel = pgBrowser.Node.addUtilityPanel(), + var panel = pgBrowser.Node.addUtilityPanel(pgBrowser.stdW.md), j = panel.$container.find('.obj_properties').first(); var schema = that.getUISchema(treeItem); @@ -159,7 +160,7 @@ define('tools.restore', [ }), extraData = that.setExtraParameters(treeNodeInfo, data); - var sqlHelpUrl = 'restore.html', + var sqlHelpUrl = 'backup.html', helpUrl = url_for('help.static', { 'filename': 'restore_dialog.html', }); diff --git a/web/pgadmin/tools/restore/static/js/restore.ui.js b/web/pgadmin/tools/restore/static/js/restore.ui.js index d14d0e9ee..e6dbeb9dd 100644 --- a/web/pgadmin/tools/restore/static/js/restore.ui.js +++ b/web/pgadmin/tools/restore/static/js/restore.ui.js @@ -100,6 +100,9 @@ export class RestoreTypeObjSchema extends BaseUISchema { group: gettext('Type of objects'), deps: ['pre_data', 'data', 'post_data', 'only_schema'], disabled: function(state) { + if(obj.selectedNodeType == 'table') { + state.only_data = true; + } return (obj.selectedNodeType !== 'database' && obj.selectedNodeType !== 'schema') || (state.pre_data || state.data || @@ -114,6 +117,9 @@ export class RestoreTypeObjSchema extends BaseUISchema { group: gettext('Type of objects'), deps: ['pre_data', 'data', 'post_data', 'only_data'], disabled: function(state) { + if(obj.selectedNodeType == 'index' || obj.selectedNodeType == 'function') { + state.only_schema = true; + } return (obj.selectedNodeType !== 'database' && obj.selectedNodeType !== 'schema') || (state.pre_data || state.data || @@ -174,7 +180,7 @@ export class RestoreSaveOptSchema extends BaseUISchema { disabled: false, group: gettext('Do not save'), visible: function() { - var serverInfo = obj.fieldOptions.nodeInfo; + var serverInfo = obj.fieldOptions.nodeInfo.server; return !_.isUndefined(serverInfo) && serverInfo.version >= 110000 ? true : false; }, }]; @@ -218,9 +224,11 @@ export class RestoreQueryOptionSchema extends BaseUISchema { label: gettext('Clean before restore'), type: 'switch', group: gettext('Queries'), - disabled: function() { - return obj.selectedNodeType === 'function' - || obj.selectedNodeType === 'trigger_function'; + disabled: function(state) { + if(obj.selectedNodeType === 'function' || obj.selectedNodeType === 'trigger_function') { + state.clean = true; + return true; + } }, }, { id: 'single_transaction', @@ -407,37 +415,37 @@ export default class RestoreSchema extends BaseUISchema { }, { type: 'nested-fieldset', label: gettext('Sections'), - group: gettext('Restore options'), + group: gettext('Data/Objects'), schema:obj.getSectionSchema(), visible: true }, { type: 'nested-fieldset', label: gettext('Type of objects'), - group: gettext('Restore options'), + group: gettext('Data/Objects'), schema:obj.getRestoreTypeObjSchema(), visible: true }, { type: 'nested-fieldset', label: gettext('Do not save'), - group: gettext('Restore options'), + group: gettext('Data/Objects'), schema:obj.getRestoreSaveOptSchema(), visible: true }, { type: 'nested-fieldset', label: gettext('Queries'), - group: gettext('Restore options'), + group: gettext('Options'), schema:obj.getRestoreQueryOptionSchema(), visible: true }, { type: 'nested-fieldset', label: gettext('Disable'), - group: gettext('Restore options'), + group: gettext('Options'), schema:obj.getRestoreDisableOptionSchema(), visible: true }, { type: 'nested-fieldset', label: gettext('Miscellaneous / Behavior'), - group: gettext('Restore options'), + group: gettext('Options'), schema:obj.getRestoreMiscellaneousSchema(), visible: true }]; diff --git a/web/regression/feature_utils/locators.py b/web/regression/feature_utils/locators.py index df606a2b1..e44eac873 100644 --- a/web/regression/feature_utils/locators.py +++ b/web/regression/feature_utils/locators.py @@ -100,7 +100,7 @@ class NavMenuLocators: "//following::input[@name='file']" restore_button_xpath = \ - "//button[contains(@class,'fa-upload') and contains(.,'Restore')]" + "//button[ contains(.,'Restore')]" maintenance_operation = "//label[text()='Maintenance operation']"