@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
870 lines (868 loc) • 34.9 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useContext, useEffect, useRef, useState } from 'react';
import { fetchActiveEnvironment } from '../../services/environment';
import { fetchConfigurationXML, fetchSiteConfigurationFiles, writeConfiguration } from '../../services/configuration';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import useStyles from './styles';
import ListSubheader from '@mui/material/ListSubheader';
import { FormattedMessage, useIntl } from 'react-intl';
import Skeleton from '@mui/material/Skeleton';
import EmptyState from '../EmptyState/EmptyState';
import { translations } from './translations';
import { getTranslation } from '../../utils/i18n';
import { LoadingState } from '../LoadingState/LoadingState';
import AceEditor from '../AceEditor/AceEditor';
import GlobalAppToolbar from '../GlobalAppToolbar';
import ResizeableDrawer from '../ResizeableDrawer/ResizeableDrawer';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import MenuOpenRoundedIcon from '@mui/icons-material/MenuOpenRounded';
import MenuRoundedIcon from '@mui/icons-material/MenuRounded';
import PrimaryButton from '../PrimaryButton';
import DialogFooter from '../DialogFooter/DialogFooter';
import SecondaryButton from '../SecondaryButton';
import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded';
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import ConfirmDialog from '../ConfirmDialog/ConfirmDialog';
import informationGraphicUrl from '../../assets/information.svg';
import Typography from '@mui/material/Typography';
import { useDispatch } from 'react-redux';
import { fetchItemVersions } from '../../state/actions/versions';
import { fetchItemByPath } from '../../services/content';
import SearchBar from '../SearchBar/SearchBar';
import Alert from '@mui/material/Alert';
import { closeConfirmDialog, showConfirmDialog, showHistoryDialog } from '../../state/actions/dialogs';
import { batchActions, dispatchDOMEvent } from '../../state/actions/misc';
import { capitalize, stripCData } from '../../utils/string';
import { itemReverted, showSystemNotification } from '../../state/actions/system';
import { getHostToHostBus } from '../../utils/subjects';
import { filter, map } from 'rxjs/operators';
import { parseValidateDocument, serialize } from '../../utils/xml';
import { forkJoin } from 'rxjs';
import { encrypt } from '../../services/security';
import { showErrorDialog } from '../../state/reducers/dialogs/error';
import ResizeBar from '../ResizeBar';
import { useSelection } from '../../hooks/useSelection';
import { useActiveSiteId } from '../../hooks/useActiveSiteId';
import { useMount } from '../../hooks/useMount';
import { findPendingEncryption } from './utils';
import useUpdateRefs from '../../hooks/useUpdateRefs';
import { MAX_CONFIG_SIZE, UNDEFINED } from '../../utils/constants';
import { ApiResponseErrorState } from '../ApiResponseErrorState';
import { nnou } from '../../utils/object';
import { MaxLengthCircularProgress } from '../MaxLengthCircularProgress';
import useUnmount from '../../hooks/useUnmount';
import useActiveUser from '../../hooks/useActiveUser';
import { createCustomDocumentEventListener } from '../../utils/dom';
import { ProjectToolsRoutes } from '../../env/routes';
import { SiteToolsContext } from '../SiteTools/siteToolsContext';
export function SiteConfigurationManagement(props) {
const { embedded, showAppsButton, onSubmittingAndOrPendingChange, isSubmitting } = props;
const site = useActiveSiteId();
const { username } = useActiveUser();
const sessionStorageKey = `craftercms.${username}.projectToolsConfigurationData.${site}`;
const baseUrl = useSelection((state) => state.env.authoringBase);
const { classes, cx: clsx } = useStyles();
const { formatMessage } = useIntl();
const [environment, setEnvironment] = useState();
const [files, setFiles] = useState();
const [selectedConfigFile, setSelectedConfigFile] = useState(
() => JSON.parse(sessionStorage.getItem(sessionStorageKey))?.selectedConfigFile ?? null
);
const [changesRecoveredOnMount] = useState(() => selectedConfigFile !== null);
const ignoreEnv = selectedConfigFile?.path === 'site-policy-config.xml';
const [selectedConfigFileXml, setSelectedConfigFileXml] = useState(null);
const [configError, setConfigError] = useState(null);
const [selectedSampleConfigFileXml, setSelectedSampleConfigFileXml] = useState(null);
const [loadingXml, setLoadingXml] = useState(true);
const [encrypting, setEncrypting] = useState(false);
const [loadingSampleXml, setLoadingSampleXml] = useState(false);
const [sampleError, setSampleError] = useState(null);
const [showSampleEditor, setShowSampleEditor] = useState(false);
const [width, setWidth] = useState(240);
const [openDrawer, setOpenDrawer] = useState(true);
const [leftEditorWidth, setLeftEditorWidth] = useState(null);
const [disabledSaveButton, setDisabledSaveButton] = useState(true);
const [confirmDialogProps, setConfirmDialogProps] = useState(null);
const [keyword, setKeyword] = useState('');
const dispatch = useDispatch();
const setTool = useContext(SiteToolsContext)?.setTool;
const refs = useUpdateRefs({
disabledSaveButton,
selectedConfigFile,
setTool
});
const functionRefs = useUpdateRefs({
onSubmittingAndOrPendingChange
});
const editorRef = useRef({
container: null
});
const [contentSize, setContentSize] = useState(0);
useMount(() => {
if (changesRecoveredOnMount) {
dispatch(
showSystemNotification({
message: formatMessage({ defaultMessage: 'Unsaved changes were restored on to the editor.' }),
options: { variant: 'info' }
})
);
}
fetchActiveEnvironment().subscribe({
next(env) {
setEnvironment(env);
},
error({ response }) {
dispatch(showErrorDialog({ error: response }));
}
});
});
useUnmount(() => {
if (!refs.current.disabledSaveButton) {
sessionStorage.setItem(
sessionStorageKey,
JSON.stringify({
content: editorRef.current.getValue(),
selectedConfigFile: refs.current.selectedConfigFile
})
);
const eventId = 'unsavedConfigurationChangesConfirmation';
const title = getTranslation(refs.current.selectedConfigFile.title, translations, formatMessage);
if (refs.current.setTool) {
dispatch(
showConfirmDialog({
body: formatMessage({ defaultMessage: 'You left unsaved changes on "{title}"' }, { title }),
onCancel: batchActions([closeConfirmDialog(), dispatchDOMEvent({ id: eventId, button: 'cancel' })]),
onOk: batchActions([closeConfirmDialog(), dispatchDOMEvent({ id: eventId, button: 'ok' })]),
okButtonText: React.createElement(FormattedMessage, { defaultMessage: 'Go back and recover changes' }),
cancelButtonText: React.createElement(FormattedMessage, { defaultMessage: 'Discard changes' })
})
);
createCustomDocumentEventListener(eventId, ({ button }) => {
if (button === 'ok') {
refs.current.setTool(ProjectToolsRoutes.Configuration);
} else {
sessionStorage.removeItem(sessionStorageKey);
}
});
} else {
dispatch(
showConfirmDialog({
body: formatMessage(
{
defaultMessage:
'You left unsaved changes on "{title}". You may go back to configuration now if you wish to recover or ignore to discard changes.'
},
{ title }
)
})
);
}
}
});
useEffect(() => {
if (site && environment !== UNDEFINED) {
fetchSiteConfigurationFiles(site, environment).subscribe({
next(files) {
setFiles(files.map((file) => ({ ...file, id: `${file.module}/${file.path}` })));
},
error({ response }) {
dispatch(showErrorDialog({ error: response.response }));
}
});
}
}, [environment, site, dispatch]);
useEffect(() => {
if (selectedConfigFile && environment) {
setConfigError(null);
const sessionData = JSON.parse(sessionStorage.getItem(sessionStorageKey));
if (sessionData?.content) {
setSelectedConfigFileXml(sessionData.content);
setContentSize(sessionData.content.length);
setDisabledSaveButton(false);
sessionStorage.removeItem(sessionStorageKey);
setLoadingXml(false);
} else {
fetchConfigurationXML(
site,
selectedConfigFile.path,
selectedConfigFile.module,
ignoreEnv ? null : environment
).subscribe({
next(xml) {
setSelectedConfigFileXml(xml ?? '');
setContentSize(xml?.length ?? 0);
setLoadingXml(false);
},
error({ response }) {
if (response.response.code === 7000) {
setSelectedConfigFileXml('');
} else {
setConfigError(response.response);
}
setLoadingXml(false);
}
});
}
}
}, [selectedConfigFile, environment, site, ignoreEnv, refs, sessionStorageKey]);
// Item Revert Propagation
useEffect(() => {
const hostToHost$ = getHostToHostBus();
const subscription = hostToHost$
.pipe(filter((e) => itemReverted.type === e.type))
.subscribe(({ type, payload }) => {
if (payload.target.endsWith(selectedConfigFile.path)) {
setSelectedConfigFile({ ...selectedConfigFile });
}
});
return () => {
subscription.unsubscribe();
};
}, [dispatch, selectedConfigFile]);
const onToggleDrawer = () => {
setOpenDrawer(!openDrawer);
};
const showXmlParseError = (error) => {
dispatch(
showSystemNotification({
message: formatMessage(translations.xmlContainsErrors, {
errors: error
}),
options: {
variant: 'error'
}
})
);
};
const onEncryptClick = () => {
const content = editorRef.current.getValue();
const doc = parseValidateDocument(content);
if (typeof doc === 'string') {
showXmlParseError(doc);
return;
}
const tags = doc.querySelectorAll('[encrypted]');
const items = findPendingEncryption(tags);
if (items.length) {
setEncrypting(true);
forkJoin(
items.map(({ tag, text }) => encrypt(stripCData(text), site).pipe(map((text) => ({ tag, text }))))
).subscribe({
next(encrypted) {
encrypted.forEach(({ text, tag }) => {
tag.innerHTML = `\${enc:${text}}`;
tag.setAttribute('encrypted', 'true');
});
editorRef.current.setValue(serialize(doc), -1);
setEncrypting(false);
},
error({ response: { response } }) {
dispatch(showErrorDialog({ error: response }));
}
});
} else {
setConfirmDialogProps({
open: true,
imageUrl: informationGraphicUrl,
title: tags.length ? formatMessage(translations.allEncrypted) : formatMessage(translations.noEncryptItems),
onOk: onConfirmDialogClose,
onClose: onConfirmDialogClose,
onClosed: onConfirmDialogClosed
});
}
};
const onEncryptHelpClick = () => {
setConfirmDialogProps({
open: true,
maxWidth: 'sm',
onOk: onConfirmDialogClose,
onClose: onConfirmDialogClose,
onClosed: onConfirmDialogClosed,
imageUrl: informationGraphicUrl,
children: React.createElement(
'section',
{ className: classes.confirmDialogBody },
React.createElement(
Typography,
{ className: classes.textMargin, variant: 'subtitle1' },
formatMessage(translations.encryptMarked)
),
React.createElement(
Typography,
{ className: classes.textMargin, variant: 'body2' },
formatMessage(translations.encryptHintPt1)
),
React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.encryptHintPt2, bold)),
React.createElement(
Typography,
{ className: classes.textMargin, variant: 'body2' },
formatMessage(translations.encryptHintPt3, tags)
),
React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.encryptHintPt4, bold)),
React.createElement(
Typography,
{ className: classes.textMargin, variant: 'body2' },
formatMessage(translations.encryptHintPt5, tagsAndCurls)
),
React.createElement(
Typography,
{ className: classes.textMargin, variant: 'body2' },
formatMessage(translations.encryptHintPt6)
),
React.createElement(
'ul',
null,
React.createElement(
'li',
null,
React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.encryptHintPt7))
),
React.createElement(
'li',
null,
React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.encryptHintPt8))
),
React.createElement(
'li',
null,
React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.encryptHintPt9))
)
)
)
});
};
const onViewSampleClick = () => {
if (showSampleEditor) {
setLeftEditorWidth(null);
}
setLoadingSampleXml(true);
setShowSampleEditor(!showSampleEditor);
if (showSampleEditor === false) {
setSampleError(null);
fetchConfigurationXML(
'studio_root',
`/configuration/samples/${selectedConfigFile.samplePath}`,
selectedConfigFile.module,
environment
).subscribe({
next(xml) {
setSelectedSampleConfigFileXml(xml);
setLoadingSampleXml(false);
},
error({ response }) {
setSampleError(response.response);
setLoadingSampleXml(false);
}
});
}
};
const onClean = () => {
if (showSampleEditor) {
setShowSampleEditor(false);
}
if (leftEditorWidth !== null) {
setLeftEditorWidth(null);
}
if (encrypting !== null) {
setEncrypting(false);
}
if (!disabledSaveButton) {
setDisabledSaveButton(true);
}
functionRefs.current.onSubmittingAndOrPendingChange?.({ hasPendingChanges: false, isSubmitting: false });
};
const onListItemClick = (file) => {
if (file.id !== selectedConfigFile?.id) {
setLoadingXml(true);
setSelectedConfigFile(file);
}
onClean();
};
const onUnsavedChangesOk = (file) => {
setConfirmDialogProps({ ...confirmDialogProps, open: false });
onListItemClick(file);
};
const onConfirmDialogClose = () => {
setConfirmDialogProps({ ...confirmDialogProps, open: false });
};
const onConfirmDialogClosed = () => {
setConfirmDialogProps(null);
};
const onEditorResize = (width) => {
if (width > 240) {
setLeftEditorWidth(width);
}
};
const onEditorChanges = () => {
const currentEditorValue = editorRef.current.getValue();
if (selectedConfigFileXml !== currentEditorValue) {
setDisabledSaveButton(false);
functionRefs.current.onSubmittingAndOrPendingChange?.({ hasPendingChanges: true });
} else {
setDisabledSaveButton(true);
functionRefs.current.onSubmittingAndOrPendingChange?.({ hasPendingChanges: false });
}
setContentSize(currentEditorValue.length);
};
const onShowHistory = () => {
fetchItemByPath(site, `/config/${selectedConfigFile.module}/${selectedConfigFile.path}`).subscribe((item) => {
dispatch(
batchActions([
fetchItemVersions({
isConfig: true,
environment: environment,
module: selectedConfigFile.module,
item
}),
showHistoryDialog({})
])
);
});
};
const onCancel = () => {
onClean();
setSelectedConfigFile(null);
};
const showUnsavedChangesConfirm = (file) => {
setConfirmDialogProps({
open: true,
title: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.unsavedChangesTitle',
defaultMessage: 'Unsaved changes'
}),
body: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.unsavedChangesSubtitle',
defaultMessage: 'You have unsaved changes, do you want to leave?'
}),
onClosed: onConfirmDialogClosed,
onOk: () => onUnsavedChangesOk(file),
onCancel: onConfirmDialogClose
});
};
const onSave = () => {
const content = editorRef.current.getValue();
const doc = parseValidateDocument(content);
if (typeof doc === 'string') {
showXmlParseError(doc);
return;
}
const unencryptedItems = findPendingEncryption(doc.querySelectorAll('[encrypted]'));
const errors = editorRef.current
.getSession()
.getAnnotations()
.filter((annotation) => {
return annotation.type === 'error';
});
if (errors.length) {
dispatch(
showSystemNotification({
message: formatMessage(translations.documentError),
options: {
variant: 'error'
}
})
);
} else {
if (unencryptedItems.length === 0) {
functionRefs.current.onSubmittingAndOrPendingChange?.({ isSubmitting: true });
writeConfiguration(
site,
selectedConfigFile.path,
selectedConfigFile.module,
content,
ignoreEnv ? null : environment
).subscribe({
next: () => {
functionRefs.current.onSubmittingAndOrPendingChange?.({ isSubmitting: false, hasPendingChanges: false });
dispatch(
showSystemNotification({
message: formatMessage(translations.configSaved)
})
);
setDisabledSaveButton(true);
setSelectedConfigFileXml(content);
},
error: ({ response: { response } }) => {
functionRefs.current.onSubmittingAndOrPendingChange?.({ isSubmitting: false });
dispatch(showErrorDialog({ error: response }));
}
});
} else {
let tags;
if (unencryptedItems.length > 1) {
tags = unencryptedItems.map((item) => {
return formatMessage(translations.encryptionSingleDetail, {
name: item.tag.tagName,
value: item.text,
br: React.createElement('br', { key: item.text })
});
});
} else {
tags = formatMessage(translations.encryptionSingleDetail, {
name: unencryptedItems[0].tag.tagName,
value: unencryptedItems[0].text,
br: null
});
}
setConfirmDialogProps({
open: true,
imageUrl: informationGraphicUrl,
title: formatMessage(translations.pendingEncryption, {
itemCount: unencryptedItems.length,
tags,
br: unencryptedItems.length ? React.createElement('br', { key: unencryptedItems.length }) : null
}),
onOk: onConfirmDialogClose,
onClose: onConfirmDialogClose,
onClosed: onConfirmDialogClosed
});
}
}
};
const bold = {
bold: (msg) => React.createElement('strong', { key: msg, className: 'bold' }, msg)
};
const tags = { lt: '<', gt: '>' };
const tagsAndCurls = Object.assign({ lc: '{', rc: '}' }, tags);
const onAceInit = (editor) => {
editor.commands.addCommand({
name: 'saveToCrafter',
bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
exec: () => onSave(),
readOnly: false
});
};
return React.createElement(
'section',
{ className: classes.root },
!embedded &&
React.createElement(GlobalAppToolbar, {
title: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.title',
defaultMessage: 'Configuration'
}),
showAppsButton: showAppsButton
}),
React.createElement(
ResizeableDrawer,
{
belowToolbar: true,
open: openDrawer,
width: width,
classes: { drawerPaper: clsx(classes.drawerPaper, embedded && 'embedded') },
onWidthChange: setWidth
},
React.createElement(
List,
{
className: classes.list,
component: 'nav',
dense: true,
subheader: React.createElement(
ListSubheader,
{ className: classes.listSubheader, component: 'div' },
environment
? React.createElement(
React.Fragment,
null,
React.createElement(
Tooltip,
{
placement: 'top',
title: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.environment',
defaultMessage: 'The active environment is "{environment}"',
values: { environment }
})
},
React.createElement(
Alert,
{ severity: 'info', className: classes.alert, classes: { message: classes.ellipsis } },
React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.activeEnvironment',
defaultMessage: '{environment} Environment',
values: { environment: capitalize(environment) }
})
)
),
React.createElement(SearchBar, {
classes: { root: classes.searchBarRoot },
keyword: keyword,
onChange: setKeyword,
showActionButton: Boolean(keyword),
autoFocus: true
})
)
: React.createElement(
'section',
{ className: classes.listSubheaderSkeleton },
React.createElement(Skeleton, { height: 34, width: '100%' }),
React.createElement(Skeleton, { height: 34, width: '100%' })
)
)
},
files
? files
.filter(
(file) =>
file.path.toLowerCase().includes(keyword) ||
getTranslation(file.title, translations, formatMessage).toLowerCase().includes(keyword) ||
getTranslation(file.description, translations, formatMessage).toLowerCase().includes(keyword)
)
.map((file, i) =>
React.createElement(
ListItem,
{
selected: file.id === selectedConfigFile?.id,
onClick: () => {
if (!disabledSaveButton && file.id !== selectedConfigFile?.id) {
showUnsavedChangesConfirm(file);
} else {
onListItemClick(file);
}
},
button: true,
key: i,
dense: true,
divider: i < files.length - 1
},
React.createElement(ListItemText, {
classes: { primary: classes.ellipsis, secondary: classes.ellipsis },
primaryTypographyProps: { title: getTranslation(file.title, translations, formatMessage) },
secondaryTypographyProps: {
title: getTranslation(file.description, translations, formatMessage)
},
primary: getTranslation(file.title, translations, formatMessage),
secondary: getTranslation(file.description, translations, formatMessage)
})
)
)
: Array(15)
.fill(null)
.map((x, i) =>
React.createElement(
ListItem,
{ button: true, key: i, dense: true, divider: i < Array.length - 1 },
React.createElement(ListItemText, {
primary: React.createElement(Skeleton, { height: 15, width: '80%' }),
secondary: React.createElement(Skeleton, { height: 15, width: '60%' }),
primaryTypographyProps: {
className: classes.itemSkeletonText
},
secondaryTypographyProps: {
className: classes.itemSkeletonText
}
})
)
)
)
),
selectedConfigFile
? React.createElement(
Box,
{
display: 'flex',
flexGrow: 1,
flexDirection: loadingXml ? 'row' : 'column',
paddingLeft: openDrawer ? `${width}px` : 0
},
configError
? React.createElement(ApiResponseErrorState, { error: configError, classes: { root: classes.errorState } })
: loadingXml
? React.createElement(LoadingState, null)
: nnou(selectedConfigFileXml)
? React.createElement(
React.Fragment,
null,
React.createElement(GlobalAppToolbar, {
classes: {
appBar: classes.appBar
},
styles: {
toolbar: { '& > section': {} }
},
showHamburgerMenuButton: false,
showAppsButton: false,
startContent: React.createElement(
IconButton,
{ onClick: onToggleDrawer, size: 'large' },
openDrawer
? React.createElement(MenuOpenRoundedIcon, null)
: React.createElement(MenuRoundedIcon, null)
),
title: getTranslation(selectedConfigFile.title, translations, formatMessage),
subtitle: getTranslation(selectedConfigFile.description, translations, formatMessage),
rightContent: React.createElement(
React.Fragment,
null,
React.createElement(
ButtonGroup,
{ variant: 'outlined', className: classes.buttonGroup },
React.createElement(
SecondaryButton,
{ disabled: encrypting, onClick: onEncryptClick, loading: encrypting },
formatMessage(translations.encryptMarked)
),
React.createElement(
Button,
{ size: 'small', onClick: onEncryptHelpClick },
React.createElement(HelpOutlineRoundedIcon, null)
)
),
React.createElement(
SecondaryButton,
{ onClick: onViewSampleClick },
showSampleEditor
? React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.hideSample',
defaultMessage: 'Hide Sample'
})
: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.viewSample',
defaultMessage: 'View Sample'
})
)
)
}),
React.createElement(
Box,
{ display: 'flex', flexGrow: 1 },
React.createElement(AceEditor, {
ref: editorRef,
styles: {
root: {
display: 'flex',
width: leftEditorWidth ? `${leftEditorWidth}px` : 'auto',
flexGrow: leftEditorWidth ? 0 : 1
},
editorRoot: {
margin: 0,
opacity: encrypting ? 0.5 : 1,
border: '0',
borderRadius: '0'
}
},
mode: 'ace/mode/xml',
theme: 'ace/theme/textmate',
readOnly: encrypting,
autoFocus: true,
onChange: onEditorChanges,
value: selectedConfigFileXml,
onInit: onAceInit
}),
showSampleEditor &&
React.createElement(
React.Fragment,
null,
React.createElement(ResizeBar, {
onWidthChange: onEditorResize,
element: editorRef.current.container
}),
sampleError
? React.createElement(ApiResponseErrorState, {
error: sampleError,
classes: { root: classes.sampleErrorState }
})
: loadingSampleXml
? React.createElement(LoadingState, null)
: nnou(selectedSampleConfigFileXml)
? React.createElement(AceEditor, {
classes: { root: classes.rootEditor, editorRoot: classes.editorRoot },
mode: 'ace/mode/xml',
theme: 'ace/theme/textmate',
autoFocus: false,
readOnly: true,
value: selectedSampleConfigFileXml
})
: React.createElement(React.Fragment, null)
)
),
React.createElement(
DialogFooter,
null,
React.createElement(
SecondaryButton,
{ disabled: encrypting, className: classes.historyButton, onClick: onShowHistory },
React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.history',
defaultMessage: 'History'
})
),
React.createElement(
SecondaryButton,
{ disabled: encrypting, onClick: onCancel },
React.createElement(FormattedMessage, { id: 'words.cancel', defaultMessage: 'Cancel' })
),
React.createElement(
PrimaryButton,
{
disabled: disabledSaveButton || encrypting || isSubmitting || contentSize > MAX_CONFIG_SIZE,
onClick: onSave
},
React.createElement(FormattedMessage, { id: 'words.save', defaultMessage: 'Save' })
),
React.createElement(MaxLengthCircularProgress, {
sxs: { circularProgress: { width: '35px !important', height: '35px !important' } },
max: MAX_CONFIG_SIZE,
current: contentSize,
renderThresholdPercentage: 90
})
)
)
: React.createElement(React.Fragment, null)
)
: React.createElement(
Box,
{
display: 'flex',
alignItems: 'center',
flexGrow: 1,
justifyContent: 'center',
paddingLeft: openDrawer && `${width}px`
},
React.createElement(EmptyState, {
title: React.createElement(FormattedMessage, {
id: 'siteConfigurationManagement.selectConfigFile',
defaultMessage: 'Please choose a config file from the left.'
}),
image: `${baseUrl}/static-assets/images/choose_option.svg`
})
),
React.createElement(ConfirmDialog, { open: false, ...confirmDialogProps })
);
}
export default SiteConfigurationManagement;