Port change ownership dialog to React. Fixes #7590

This commit is contained in:
Akshay Joshi
2022-08-05 14:05:53 +05:30
parent 060d7ba46b
commit 53887c32bf
18 changed files with 264 additions and 390 deletions

View File

@@ -0,0 +1,88 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/core';
import React from 'react';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import BaseUISchema from '../SchemaView/base_schema.ui';
import SchemaView from '../SchemaView';
import { isEmptyString } from 'sources/validators';
class ChangeOwnershipSchema extends BaseUISchema {
constructor(deletedUser, adminUserList, noOfSharedServers) {
super({
newUser: '',
});
this.deletedUser = deletedUser;
this.adminUserList = adminUserList;
this.noOfSharedServers = noOfSharedServers;
}
get baseFields() {
let self = this;
return [
{
id: 'note', type: 'note',
text: gettext('Select the user that will take ownership of the shared servers created by <b>' + self.deletedUser + '</b>. <b>' + self.noOfSharedServers + '</b> shared servers are currently owned by this user.'),
}, {
id: 'newUser', label: gettext('User'),
type: 'select', controlProps: {allowClear: true},
options: self.adminUserList,
helpMessage: gettext('Note: If no user is selected, the shared servers will be deleted.')
}
];
}
validate(state) {
let obj = this;
/* mview definition validation*/
if (isEmptyString(state.newUser)) {
obj.warningText = gettext('The shared servers owned by <b>'+ obj.deletedUser +'</b> will be deleted. Do you wish to continue?');
} else {
obj.warningText = null;
}
}
}
const useStyles = makeStyles((theme)=>({
root: {
...theme.mixins.tabPanel,
},
}));
export default function ChangeOwnershipContent({onSave, onClose, deletedUser, userList, noOfSharedServers}) {
const classes = useStyles();
const objChangeOwnership = new ChangeOwnershipSchema(deletedUser, userList, noOfSharedServers);
return<SchemaView
formType={'dialog'}
getInitData={() => { /*This is intentional (SonarQube)*/ }}
schema={objChangeOwnership}
viewHelperProps={{
mode: 'create',
}}
customSaveBtnName={'Change'}
onSave={onSave}
onClose={onClose}
hasSQL={false}
disableSqlHelp={true}
disableDialogHelp={true}
isTabView={false}
formClassName={classes.root}
/>;
}
ChangeOwnershipContent.propTypes = {
onSave: PropTypes.func,
onClose: PropTypes.func,
currentUser: PropTypes.string,
userList: PropTypes.array,
noOfSharedServers: PropTypes.number,
deletedUser: PropTypes.string
};

View File

@@ -0,0 +1,100 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { makeStyles } from '@material-ui/core';
import React from 'react';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import BaseUISchema from '../SchemaView/base_schema.ui';
import SchemaView from '../SchemaView';
class ChangePasswordSchema extends BaseUISchema {
constructor(user, isPgpassFileUsed) {
super({
user: user,
password: '',
newPassword: '',
confirmPassword: ''
});
this.isPgpassFileUsed = isPgpassFileUsed;
}
get baseFields() {
let self = this;
return [
{
id: 'user', label: gettext('User'), type: 'text', disabled: true
}, {
id: 'password', label: gettext('Current Password'), type: 'password',
disabled: self.isPgpassFileUsed, noEmpty: self.isPgpassFileUsed ? false : true,
controlProps: {
maxLength: null
}
}, {
id: 'newPassword', label: gettext('New Password'), type: 'password',
noEmpty: true,
controlProps: {
maxLength: null
}
}, {
id: 'confirmPassword', label: gettext('Confirm Password'), type: 'password',
noEmpty: true,
controlProps: {
maxLength: null
}
}
];
}
validate(state, setError) {
let errmsg = null;
if (state.newPassword !== state.confirmPassword) {
errmsg = gettext('Passwords do not match.');
setError('confirmPassword', errmsg);
return true;
} else {
setError('confirmPassword', null);
}
return false;
}
}
const useStyles = makeStyles((theme)=>({
root: {
...theme.mixins.tabPanel,
},
}));
export default function ChangePasswordContent({onSave, onClose, userName, isPgpassFileUsed}) {
const classes = useStyles();
return<SchemaView
formType={'dialog'}
getInitData={() => { /*This is intentional (SonarQube)*/ }}
schema={new ChangePasswordSchema(userName, isPgpassFileUsed)}
viewHelperProps={{
mode: 'create',
}}
customSaveBtnName={'Change'}
onSave={onSave}
onClose={onClose}
hasSQL={false}
disableSqlHelp={true}
disableDialogHelp={true}
isTabView={false}
formClassName={classes.root}
/>;
}
ChangePasswordContent.propTypes = {
onSave: PropTypes.func,
onClose: PropTypes.func,
userName: PropTypes.string,
isPgpassFileUsed: PropTypes.bool
};

View File

@@ -0,0 +1,138 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React, { useState, useRef, useEffect } from 'react';
import gettext from 'sources/gettext';
import { Box } from '@material-ui/core';
import { DefaultButton, PrimaryButton } from '../components/Buttons';
import CloseIcon from '@material-ui/icons/CloseRounded';
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
import PropTypes from 'prop-types';
import { useModalStyles } from '../helpers/ModalProvider';
import { FormFooterMessage, InputCheckbox, InputText, MESSAGE_TYPE } from '../components/FormComponents';
export default function ConnectServerContent({closeModal, data, onOK, setHeight}) {
const classes = useModalStyles();
const containerRef = useRef();
const firstEleRef = useRef();
const okBtnRef = useRef();
const [formData, setFormData] = useState({
tunnel_password: '',
save_tunnel_password: false,
password: '',
save_password: false,
});
const onTextChange = (e, id) => {
let val = e;
if(e && e.target) {
val = e.target.value;
}
setFormData((prev)=>({...prev, [id]: val}));
};
const onKeyDown = (e) => {
// If enter key is pressed then click on OK button
if (e.key === 'Enter') {
okBtnRef.current?.click();
}
};
useEffect(()=>{
setTimeout(()=>{
firstEleRef.current && firstEleRef.current.focus();
}, 275);
}, []);
useEffect(()=>{
setHeight?.(containerRef.current?.offsetHeight);
}, [containerRef.current]);
if(!data) {
return <>No data</>;
}
return (
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
<Box flexGrow="1" p={2}>
{data.prompt_tunnel_password && <>
<Box>
<span style={{fontWeight: 'bold'}}>
{data.tunnel_identity_file ?
gettext('Please enter the SSH Tunnel password for the identity file \'%s\' to connect the server "%s"', data.tunnel_identity_file, data.tunnel_host)
: gettext('Please enter the SSH Tunnel password for the user \'%s\' to connect the server "%s"', data.tunnel_username, data.tunnel_host)
}
</span>
</Box>
<Box marginTop='12px'>
<InputText inputRef={firstEleRef} type="password" value={formData['tunnel_password']} controlProps={{maxLength:null}}
onChange={(e)=>onTextChange(e, 'tunnel_password')} onKeyDown={(e)=>onKeyDown(e)} />
</Box>
<Box marginTop='12px' marginBottom='12px'>
<InputCheckbox controlProps={{label: gettext('Save Password')}} value={formData['save_tunnel_password']}
onChange={(e)=>onTextChange(e.target.checked, 'save_tunnel_password')} disabled={!data.allow_save_tunnel_password} />
</Box>
</>}
{data.prompt_password && <>
<Box>
<span style={{fontWeight: 'bold'}}>
{data.username ?
gettext('Please enter the password for the user \'%s\' to connect the server - "%s"', data.username, data.server_label)
: gettext('Please enter the password for the user to connect the server - "%s"', data.server_label)
}
</span>
</Box>
<Box marginTop='12px'>
<InputText inputRef={(ele)=>{
if(!data.prompt_tunnel_password) {
/* Set only if no tunnel password asked */
firstEleRef.current = ele;
}
}} type="password" value={formData['password']} controlProps={{maxLength:null}}
onChange={(e)=>onTextChange(e, 'password')} onKeyDown={(e)=>onKeyDown(e)}/>
</Box>
<Box marginTop='12px'>
<InputCheckbox controlProps={{label: gettext('Save Password')}} value={formData['save_password']}
onChange={(e)=>onTextChange(e.target.checked, 'save_password')} disabled={!data.allow_save_password} />
</Box>
</>}
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={data.errmsg} closable={false} style={{
position: 'unset', padding: '12px 0px 0px'
}}/>
</Box>
<Box className={classes.footer}>
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
closeModal();
}} >{gettext('Cancel')}</DefaultButton>
<PrimaryButton ref={okBtnRef} data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
let postFormData = new FormData();
if(data.prompt_tunnel_password) {
postFormData.append('tunnel_password', formData.tunnel_password);
formData.save_tunnel_password &&
postFormData.append('save_tunnel_password', formData.save_tunnel_password);
}
if(data.prompt_password) {
postFormData.append('password', formData.password);
formData.save_password &&
postFormData.append('save_password', formData.save_password);
}
onOK?.(postFormData);
closeModal();
}} >{gettext('OK')}</PrimaryButton>
</Box>
</Box>
);
}
ConnectServerContent.propTypes = {
closeModal: PropTypes.func,
data: PropTypes.object,
onOK: PropTypes.func,
setHeight: PropTypes.func
};

View File

@@ -0,0 +1,124 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import { Box } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/CloseRounded';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
import HelpIcon from '@material-ui/icons/Help';
import { DefaultButton, PrimaryButton, PgIconButton } from '../components/Buttons';
import { useModalStyles } from '../helpers/ModalProvider';
import { FormFooterMessage, InputText, MESSAGE_TYPE } from '../components/FormComponents';
export default function MasterPasswordContent({ closeModal, onResetPassowrd, onOK, onCancel, setHeight, isPWDPresent, data}) {
const classes = useModalStyles();
const containerRef = useRef();
const firstEleRef = useRef();
const okBtnRef = useRef();
const [formData, setFormData] = useState({
password: ''
});
const onTextChange = (e, id) => {
let val = e;
if (e && e.target) {
val = e.target.value;
}
setFormData((prev) => ({ ...prev, [id]: val }));
};
const onKeyDown = (e) => {
// If enter key is pressed then click on OK button
if (e.key === 'Enter') {
okBtnRef.current?.click();
}
};
useEffect(() => {
setTimeout(() => {
firstEleRef.current && firstEleRef.current.focus();
}, 275);
}, []);
useEffect(() => {
setHeight?.(containerRef.current?.offsetHeight);
}, [containerRef.current]);
return (
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
<Box flexGrow="1" p={2}>
<Box>
<span style={{ fontWeight: 'bold' }}>
{isPWDPresent ? gettext('Please enter your master password.') : gettext('Please set a master password for pgAdmin.')}
</span>
<br />
<span style={{ fontWeight: 'bold' }}>
{isPWDPresent ? gettext('This is required to unlock saved passwords and reconnect to the database server(s).') : gettext('This will be used to secure and later unlock saved passwords and other credentials.')}
</span>
</Box>
<Box marginTop='12px'>
<InputText inputRef={firstEleRef} type="password" value={formData['password']} maxLength={null}
onChange={(e) => onTextChange(e, 'password')} onKeyDown={(e) => onKeyDown(e)}/>
</Box>
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={data.errmsg} closable={false} style={{
position: 'unset', padding: '12px 0px 0px'
}} />
</Box>
<Box className={classes.footer}>
<Box style={{ marginRight: 'auto' }}>
<PgIconButton data-test="help-masterpassword" title={gettext('Help')} style={{ padding: '0.3rem', paddingLeft: '0.7rem' }} startIcon={<HelpIcon />} onClick={() => {
let _url = url_for('help.static', {
'filename': 'master_password.html',
});
window.open(_url, 'pgadmin_help');
}} >
</PgIconButton>
{isPWDPresent &&
<DefaultButton data-test="reset-masterpassword" style={{ marginLeft: '0.5rem' }} startIcon={<DeleteForeverIcon />}
onClick={() => {onResetPassowrd?.();}} >
{gettext('Reset Master Password')}
</DefaultButton>
}
</Box>
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={() => {
onCancel?.();
closeModal();
}} >{gettext('Cancel')}</DefaultButton>
<PrimaryButton ref={okBtnRef} data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />}
disabled={formData.password.length == 0}
onClick={() => {
let postFormData = new FormData();
postFormData.append('password', formData.password);
postFormData.append('submit_password', true);
onOK?.(postFormData);
closeModal();
}}
>
{gettext('OK')}
</PrimaryButton>
</Box>
</Box>);
}
MasterPasswordContent.propTypes = {
closeModal: PropTypes.func,
onResetPassowrd: PropTypes.func,
onOK: PropTypes.func,
onCancel: PropTypes.func,
setHeight: PropTypes.func,
isPWDPresent: PropTypes.bool,
data: PropTypes.object,
};

View File

@@ -0,0 +1,90 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React, { useState, useRef, useEffect } from 'react';
import gettext from 'sources/gettext';
import { Box } from '@material-ui/core';
import { DefaultButton, PrimaryButton } from '../components/Buttons';
import CloseIcon from '@material-ui/icons/CloseRounded';
import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
import PropTypes from 'prop-types';
import { useModalStyles } from '../helpers/ModalProvider';
import { InputText } from '../components/FormComponents';
import { isEmptyString } from '../../../static/js/validators';
export default function NamedRestoreContent({closeModal, onOK, setHeight}) {
const classes = useModalStyles();
const containerRef = useRef();
const firstEleRef = useRef();
const okBtnRef = useRef();
const [formData, setFormData] = useState({
namedRestorePoint: ''
});
const onTextChange = (e, id) => {
let val = e;
if(e && e.target) {
val = e.target.value;
}
setFormData((prev)=>({...prev, [id]: val}));
};
const onKeyDown = (e) => {
// If enter key is pressed then click on OK button
if (e.key === 'Enter') {
okBtnRef.current?.click();
}
};
useEffect(()=>{
setTimeout(()=>{
firstEleRef.current && firstEleRef.current.focus();
}, 275);
}, []);
useEffect(()=>{
setHeight?.(containerRef.current?.offsetHeight);
}, [containerRef.current]);
const isOKDisabled = isEmptyString(formData.namedRestorePoint);
return (
<Box display="flex" flexDirection="column" className={classes.container} ref={containerRef}>
<Box flexGrow="1" p={2}>
<Box>
<span style={{fontWeight: 'bold'}}>
{gettext('Enter the name of the restore point to add')}
</span>
</Box>
<Box marginTop='12px'>
<InputText inputRef={firstEleRef} type="text" value={formData['namedRestorePoint']}
onChange={(e)=>onTextChange(e, 'namedRestorePoint')} onKeyDown={(e)=>onKeyDown(e)}/>
</Box>
</Box>
<Box className={classes.footer}>
<DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
closeModal();
}} >{gettext('Cancel')}</DefaultButton>
<PrimaryButton ref={okBtnRef} data-test="save" disabled={isOKDisabled} className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
let postFormData = new FormData();
postFormData.append('value', formData.namedRestorePoint);
onOK?.(postFormData);
closeModal();
}} >{gettext('OK')}</PrimaryButton>
</Box>
</Box>
);
}
NamedRestoreContent.propTypes = {
closeModal: PropTypes.func,
data: PropTypes.object,
onOK: PropTypes.func,
setHeight: PropTypes.func
};

View File

@@ -0,0 +1,349 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import ReactDOM from 'react-dom';
import pgAdmin from 'sources/pgadmin';
import ConnectServerContent from './ConnectServerContent';
import Theme from 'sources/Theme';
import url_for from 'sources/url_for';
import gettext from 'sources/gettext';
import getApiInstance from '../api_instance';
import Notify from '../helpers/Notifier';
import MasterPasswordContent from './MasterPassowrdContent';
import ChangePasswordContent from './ChangePassowrdContent';
import NamedRestoreContent from './NamedRestoreContent';
import ChangeOwnershipContent from './ChangeOwnershipContent';
function mountDialog(title, getDialogContent, docker=undefined) {
// Register dialog panel
var panel;
if (docker) {
pgAdmin.Browser.Node.registerUtilityPanel(docker);
panel = pgAdmin.Browser.Node.addUtilityPanel(pgAdmin.Browser.stdW.md, undefined, docker);
} else {
pgAdmin.Browser.Node.registerUtilityPanel();
panel = pgAdmin.Browser.Node.addUtilityPanel(pgAdmin.Browser.stdW.md);
}
var j = panel.$container.find('.obj_properties').first();
panel.title(title);
const onClose = ()=> {
ReactDOM.unmountComponentAtNode(j[0]);
panel.close();
};
const setNewSize = (width, height)=> {
// Add height of the header
let newHeight = height + 31;
// Set min and max size of the panel
panel.minSize(width, newHeight);
panel.maxSize(width, newHeight);
panel.maximisable(false);
/* No other way to update size, below is the only way */
panel._parent._size.x = width;
panel._parent._size.y = newHeight;
panel._parent.__update();
};
ReactDOM.render(getDialogContent(onClose, setNewSize), j[0]);
}
// This functions is used to show the connect server password dialog.
export function showServerPassword() {
var title = arguments[0],
formJson = arguments[1],
nodeObj = arguments[2],
nodeData = arguments[3],
treeNodeInfo = arguments[4],
itemNodeData = arguments[5],
status = arguments[6],
onSuccess = arguments[7],
onFailure = arguments[8];
mountDialog(title, (onClose, setNewSize)=> {
return <Theme>
<ConnectServerContent
setHeight={(containerHeight)=>{
setNewSize(pgAdmin.Browser.stdW.md, containerHeight);
}}
closeModal={()=>{
onClose();
}}
data={formJson}
onOK={(formData)=>{
const api = getApiInstance();
var _url = nodeObj.generate_url(itemNodeData, 'connect', nodeData, true);
if (!status) {
treeNodeInfo.setLeaf(itemNodeData);
treeNodeInfo.removeIcon(itemNodeData);
treeNodeInfo.addIcon(itemNodeData, {icon: 'icon-server-connecting'});
}
api.post(_url, formData)
.then(res=>{
onClose();
return onSuccess(
res.data, nodeObj, nodeData, treeNodeInfo, itemNodeData, status
);
})
.catch((err)=>{
return onFailure(
err.response.request, status, err, nodeObj, nodeData, treeNodeInfo,
itemNodeData, status
);
});
}}
/>
</Theme>;
});
}
// This functions is used to show the connect server password dialog when
// launch from Schema Diff tool.
export function showSchemaDiffServerPassword() {
var docker = arguments[0],
title = arguments[1],
formJson = arguments[2],
serverID = arguments[3],
successCallback = arguments[4],
onSuccess = arguments[5],
onFailure = arguments[6];
mountDialog(title, (onClose, setNewSize)=> {
return <Theme>
<ConnectServerContent
setHeight={(containerHeight)=>{
setNewSize(pgAdmin.Browser.stdW.md, containerHeight);
}}
closeModal={()=>{
onClose();
}}
data={formJson}
onOK={(formData)=>{
const api = getApiInstance();
var _url = url_for('schema_diff.connect_server', {'sid': serverID});
api.post(_url, formData)
.then(res=>{
onClose();
return onSuccess(res.data, successCallback);
})
.catch((err)=>{
return onFailure(
err.response.request, status, err, serverID, successCallback
);
});
}}
/>
</Theme>;
}, docker);
}
function masterPassCallbacks(masterpass_callback_queue) {
while(masterpass_callback_queue.length > 0) {
let callback = masterpass_callback_queue.shift();
callback();
}
}
export function checkMasterPassword(data, masterpass_callback_queue, cancel_callback) {
const api = getApiInstance();
api.post(url_for('browser.set_master_password'), data).then((res)=> {
if(!res.data.data.present) {
showMasterPassword(res.data.data.reset, res.data.data.errmsg, masterpass_callback_queue, cancel_callback);
} else {
masterPassCallbacks(masterpass_callback_queue);
}
}).catch(function(xhr, status, error) {
Notify.pgRespErrorNotify(xhr, error);
});
}
// This functions is used to show the master password dialog.
export function showMasterPassword(isPWDPresent, errmsg=null, masterpass_callback_queue, cancel_callback) {
const api = getApiInstance();
let title = isPWDPresent ? gettext('Unlock Saved Passwords') : gettext('Set Master Password');
mountDialog(title, (onClose, setNewSize)=> {
return <Theme>
<MasterPasswordContent
isPWDPresent= {isPWDPresent}
data={{'errmsg': errmsg}}
setHeight={(containerHeight) => {
setNewSize(pgAdmin.Browser.stdW.md, containerHeight);
}}
closeModal={() => {
onClose();
}}
onResetPassowrd={()=>{
Notify.confirm(gettext('Reset Master Password'),
gettext('This will remove all the saved passwords. This will also remove established connections to '
+ 'the server and you may need to reconnect again. Do you wish to continue?'),
function() {
var _url = url_for('browser.reset_master_password');
api.delete(_url)
.then(() => {
onClose();
showMasterPassword(false, null, masterpass_callback_queue, cancel_callback);
})
.catch((err) => {
Notify.error(err.message);
});
return true;
},
function() {/* If user clicks No */ return true;}
);
}}
onCancel={()=>{
cancel_callback?.();
}}
onOK={(formData) => {
onClose();
checkMasterPassword(formData, masterpass_callback_queue, cancel_callback);
}}
/>
</Theme>;
});
}
export function showChangeServerPassword() {
var title = arguments[0],
nodeData = arguments[1],
nodeObj = arguments[2],
itemNodeData = arguments[3],
isPgPassFileUsed = arguments[4];
mountDialog(title, (onClose)=> {
return <Theme>
<ChangePasswordContent
onClose={()=>{
onClose();
}}
onSave={(isNew, data)=>{
return new Promise((resolve, reject)=>{
const api = getApiInstance();
var _url = nodeObj.generate_url(itemNodeData, 'change_password', nodeData, true);
api.post(_url, data)
.then(({data: respData})=>{
Notify.success(respData.info);
// Notify user to update pgpass file
if(isPgPassFileUsed) {
Notify.alert(
gettext('Change Password'),
gettext('Please make sure to disconnect the server'
+ ' and update the new password in the pgpass file'
+ ' before performing any other operation')
);
}
resolve(respData.data);
onClose();
})
.catch((error)=>{
reject(error);
});
});
}}
userName={nodeData.user.name}
isPgpassFileUsed={isPgPassFileUsed}
/>
</Theme>;
});
}
export function showNamedRestorePoint() {
var title = arguments[0],
nodeData = arguments[1],
nodeObj = arguments[2],
itemNodeData = arguments[3];
mountDialog(title, (onClose, setNewSize)=> {
return <Theme>
<NamedRestoreContent
setHeight={(containerHeight)=>{
setNewSize(pgAdmin.Browser.stdW.md, containerHeight);
}}
closeModal={()=>{
onClose();
}}
onOK={(formData)=>{
const api = getApiInstance();
var _url = nodeObj.generate_url(itemNodeData, 'restore_point', nodeData, true);
api.post(_url, formData)
.then(res=>{
onClose();
Notify.success(res.data.data.result);
})
.catch(function(xhr, status, error) {
Notify.pgRespErrorNotify(xhr, error);
});
}}
/>
</Theme>;
});
}
export function showChangeOwnership() {
var title = arguments[0],
userList = arguments[1],
noOfSharedServers = arguments[2],
deletedUser = arguments[3],
destroyUserManagement = arguments[4];
// Render Preferences component
Notify.showModal(title, (onClose) => {
return <ChangeOwnershipContent
onClose={()=>{
onClose();
}}
onSave={(isNew, data)=>{
const api = getApiInstance();
return new Promise((resolve, reject)=>{
if (data.newUser == '') {
api.delete(url_for('user_management.user', {uid: deletedUser['uid']}))
.then(() => {
Notify.success(gettext('User deleted.'));
onClose();
destroyUserManagement();
resolve();
})
.catch((err)=>{
Notify.error(err);
reject(err);
});
} else {
let newData = {'new_owner': `${data.newUser}`, 'old_owner': `${deletedUser['uid']}`};
api.post(url_for('user_management.change_owner'), newData)
.then(({data: respData})=>{
Notify.success(gettext(respData.info));
onClose();
destroyUserManagement();
resolve(respData.data);
})
.catch((err)=>{
reject(err);
});
}
});
}}
userList = {userList}
noOfSharedServers = {noOfSharedServers}
deletedUser = {deletedUser['name']}
/>;
},
{ isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true,
dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md});
}

View File

@@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////
import { Box, Dialog, DialogContent, DialogTitle, makeStyles, Paper } from '@material-ui/core';
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import clsx from 'clsx';
import { getEpoch } from 'sources/utils';
import { DefaultButton, PgIconButton, PrimaryButton } from '../components/Buttons';
@@ -296,7 +296,7 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
</Box>
</DialogTitle>
<DialogContent height="100%">
{content(closeModal)}
{useMemo(()=>{ return content(closeModal); }, [])}
</DialogContent>
</Dialog>
);