mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Port change ownership dialog to React. Fixes #7590
This commit is contained in:
88
web/pgadmin/static/js/Dialogs/ChangeOwnershipContent.jsx
Normal file
88
web/pgadmin/static/js/Dialogs/ChangeOwnershipContent.jsx
Normal 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
|
||||
};
|
||||
100
web/pgadmin/static/js/Dialogs/ChangePassowrdContent.jsx
Normal file
100
web/pgadmin/static/js/Dialogs/ChangePassowrdContent.jsx
Normal 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
|
||||
};
|
||||
138
web/pgadmin/static/js/Dialogs/ConnectServerContent.jsx
Normal file
138
web/pgadmin/static/js/Dialogs/ConnectServerContent.jsx
Normal 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
|
||||
};
|
||||
124
web/pgadmin/static/js/Dialogs/MasterPassowrdContent.jsx
Normal file
124
web/pgadmin/static/js/Dialogs/MasterPassowrdContent.jsx
Normal 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,
|
||||
};
|
||||
90
web/pgadmin/static/js/Dialogs/NamedRestoreContent.jsx
Normal file
90
web/pgadmin/static/js/Dialogs/NamedRestoreContent.jsx
Normal 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
|
||||
};
|
||||
349
web/pgadmin/static/js/Dialogs/index.jsx
Normal file
349
web/pgadmin/static/js/Dialogs/index.jsx
Normal 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});
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user