Merge branch 'develop' into bugfix/deletion-with-hotkey
This commit is contained in:
commit
013d6165a7
@ -75,7 +75,7 @@
|
||||
"DeleteThemeNotice": "The theme will be deleted permanently. You will not be able to undo this action.",
|
||||
"DeveloperTools": "Developer",
|
||||
"Disabled": "Disabled",
|
||||
"DownloadCopy": "Download the copy",
|
||||
"DownloadCopy": "Download copy",
|
||||
"DownloadReportBtnText": "Download report",
|
||||
"DownloadReportDescription": "The report will be saved to My Documents",
|
||||
"DownloadStatisticsText": "You can download the report for the data available during the selected storage period to view the detailed statistics.",
|
||||
@ -86,6 +86,7 @@
|
||||
"EnableAutomaticBackup": "Enable automatic backup.",
|
||||
"EnableAutomaticBackupDescription": "Use this option to back up the space data.",
|
||||
"EnterTitle": "Enter title",
|
||||
"EnterPath": "Enter path",
|
||||
"EveryDay": "Every day",
|
||||
"EveryMonth": "Every month",
|
||||
"EveryWeek": "Every week",
|
||||
|
@ -85,6 +85,7 @@
|
||||
"EnableAutomaticBackup": "Давать возможность автоматически копировать данные",
|
||||
"EnableAutomaticBackupDescription": "Используйте эту опцию для выполнения резервного копирования данных портала.",
|
||||
"EnterTitle": "Укажите название",
|
||||
"EnterPath": "Введите путь",
|
||||
"EveryDay": "Каждый день",
|
||||
"EveryMonth": "Каждый месяц",
|
||||
"EveryWeek": "Каждую неделю",
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
} from "../../../utils/commonSettingsStyles";
|
||||
import globalColors from "@docspace/components/utils/globalColors";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import { mobile } from "@docspace/components/utils/device";
|
||||
import { hugeMobile, tablet, mobile } from "@docspace/components/utils/device";
|
||||
|
||||
const linkColor = globalColors.black;
|
||||
|
||||
@ -72,14 +72,34 @@ const StyledManualBackup = styled.div`
|
||||
.manual-backup_buttons {
|
||||
margin-top: 16px;
|
||||
margin-left: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
button:first-child {
|
||||
width: 50%;
|
||||
max-width: 164px;
|
||||
max-width: 124px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
button:last-child {
|
||||
max-width: 164px;
|
||||
width: calc(50% - 8px);
|
||||
max-width: 153px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
button:first-child {
|
||||
max-width: 129px;
|
||||
}
|
||||
button:last-child {
|
||||
max-width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${hugeMobile} {
|
||||
button:first-child {
|
||||
max-width: 155px;
|
||||
}
|
||||
button:last-child {
|
||||
max-width: 155px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.manual-backup_storages-module {
|
||||
|
@ -284,7 +284,7 @@ class ManualBackup extends React.Component {
|
||||
{isCheckedTemporaryStorage && (
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={this.onMakeTemporaryBackup}
|
||||
primary
|
||||
isDisabled={!isMaxProgress}
|
||||
|
@ -87,7 +87,7 @@ class RoomsModule extends React.Component {
|
||||
</div>
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={this.onMakeCopy}
|
||||
primary
|
||||
isDisabled={isModuleDisabled || !selectedFolder}
|
||||
|
@ -145,7 +145,7 @@ class ThirdPartyModule extends React.Component {
|
||||
|
||||
{connectedThirdPartyAccount?.id && isTheSameThirdPartyAccount && (
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={this.onMakeCopy}
|
||||
primary
|
||||
isDisabled={isModuleDisabled || selectedFolder === ""}
|
||||
|
@ -50,7 +50,7 @@ class AmazonStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
isDisabled={!isValidForm || !isMaxProgress || this.isDisabled}
|
||||
|
@ -47,7 +47,7 @@ class GoogleCloudStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
isDisabled={!isValidForm || !isMaxProgress || this.isDisabled}
|
||||
|
@ -48,7 +48,7 @@ class RackspaceStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
isDisabled={!isValidForm || !isMaxProgress || this.isDisabled}
|
||||
|
@ -47,7 +47,7 @@ class SelectelStorage extends React.Component {
|
||||
|
||||
<div className="manual-backup_buttons">
|
||||
<Button
|
||||
label={t("Common:Duplicate")}
|
||||
label={t("Common:CreateCopy")}
|
||||
onClick={onMakeCopyIntoStorage}
|
||||
primary
|
||||
isDisabled={!isValidForm || !isMaxProgress || this.isDisabled}
|
||||
|
@ -1,29 +1,34 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import Button from "@docspace/components/button";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import Text from "@docspace/components/text";
|
||||
import RadioButton from "@docspace/components/radio-button";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { startRestore } from "@docspace/common/api/portal";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
import { BackupStorageType, TenantStatus } from "@docspace/common/constants";
|
||||
import { request } from "@docspace/common/api/client";
|
||||
import { StyledRestoreBackup } from "./../StyledBackup";
|
||||
import BackupListModalDialog from "./sub-components/backup-list";
|
||||
import RoomsModule from "./sub-components/RoomsModule";
|
||||
import ThirdPartyResources from "./sub-components/ThirdPartyResourcesModule";
|
||||
import ThirdPartyStorages from "./sub-components/ThirdPartyStoragesModule";
|
||||
import LocalFile from "./sub-components/LocalFileModule";
|
||||
import config from "PACKAGE_FILE";
|
||||
|
||||
import { getSettingsThirdParty } from "@docspace/common/api/files";
|
||||
import {
|
||||
getBackupStorage,
|
||||
getStorageRegions,
|
||||
} from "@docspace/common/api/settings";
|
||||
import RestoreBackupLoader from "@docspace/common/components/Loaders/RestoreBackupLoader";
|
||||
import FloatingButton from "@docspace/common/components/FloatingButton";
|
||||
import { getSettingsThirdParty } from "@docspace/common/api/files";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import RadioButtonGroup from "@docspace/components/radio-button-group";
|
||||
import { BackupStorageType } from "@docspace/common/constants";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
import LocalFileModule from "./sub-components/LocalFileModule";
|
||||
import ThirdPartyStoragesModule from "./sub-components/ThirdPartyStoragesModule";
|
||||
import ThirdPartyResourcesModule from "./sub-components/ThirdPartyResourcesModule";
|
||||
import BackupListModalDialog from "./sub-components/backup-list";
|
||||
import RoomsModule from "./sub-components/RoomsModule";
|
||||
import ButtonContainer from "./sub-components/ButtonComponent";
|
||||
import { StyledRestoreBackup } from "../StyledBackup";
|
||||
|
||||
const LOCAL_FILE = "localFile",
|
||||
BACKUP_ROOM = "backupRoom",
|
||||
DISK_SPACE = "thirdPartyDiskSpace",
|
||||
STORAGE_SPACE = "thirdPartyStorageSpace";
|
||||
|
||||
const NOTIFICATION = "notification",
|
||||
CONFIRMATION = "confirmation";
|
||||
|
||||
const {
|
||||
DocumentModuleType,
|
||||
@ -31,47 +36,38 @@ const {
|
||||
StorageModuleType,
|
||||
LocalFileModuleType,
|
||||
} = BackupStorageType;
|
||||
class RestoreBackup extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isChecked: false,
|
||||
isNotify: true,
|
||||
isVisibleDialog: false,
|
||||
isPanelVisible: false,
|
||||
isCheckedDocuments: false,
|
||||
isCheckedThirdParty: false,
|
||||
isCheckedThirdPartyStorage: false,
|
||||
isCheckedLocalFile: true,
|
||||
selectedFileId: "",
|
||||
selectedFile: "",
|
||||
|
||||
isFileSelectedError: false,
|
||||
isInitialLoading: true,
|
||||
checkingRecoveryData: false,
|
||||
};
|
||||
const RestoreBackup = (props) => {
|
||||
const {
|
||||
getProgress,
|
||||
t,
|
||||
setThirdPartyStorage,
|
||||
setStorageRegions,
|
||||
setConnectedThirdPartyAccount,
|
||||
clearProgressInterval,
|
||||
isEnableRestore,
|
||||
setRestoreResource,
|
||||
buttonSize,
|
||||
history,
|
||||
} = props;
|
||||
|
||||
this.switches = [
|
||||
"isCheckedLocalFile",
|
||||
"isCheckedDocuments",
|
||||
"isCheckedThirdParty",
|
||||
"isCheckedThirdPartyStorage",
|
||||
];
|
||||
|
||||
this.storageId = "";
|
||||
}
|
||||
|
||||
setBasicSettings = async () => {
|
||||
const {
|
||||
getProgress,
|
||||
t,
|
||||
setThirdPartyStorage,
|
||||
setStorageRegions,
|
||||
setConnectedThirdPartyAccount,
|
||||
} = this.props;
|
||||
const [radioButtonState, setRadioButtonState] = useState(LOCAL_FILE);
|
||||
const [checkboxState, setCheckboxState] = useState({
|
||||
notification: true,
|
||||
confirmation: false,
|
||||
});
|
||||
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
||||
const [isVisibleBackupListDialog, setIsVisibleBackupListDialog] = useState(
|
||||
false
|
||||
);
|
||||
const [isVisibleSelectFileDialog, setIsVisibleSelectFileDialog] = useState(
|
||||
false
|
||||
);
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
getProgress(t);
|
||||
|
||||
const [account, backupStorage, storageRegions] = await Promise.all([
|
||||
getSettingsThirdParty(),
|
||||
getBackupStorage(),
|
||||
@ -82,439 +78,223 @@ class RestoreBackup extends React.Component {
|
||||
setThirdPartyStorage(backupStorage);
|
||||
setStorageRegions(storageRegions);
|
||||
|
||||
this.setState({
|
||||
isInitialLoading: false,
|
||||
});
|
||||
setIsInitialLoading(false);
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
|
||||
this.setState({
|
||||
isInitialLoading: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.setBasicSettings();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { clearProgressInterval } = this.props;
|
||||
clearProgressInterval();
|
||||
}
|
||||
onChangeCheckbox = () => {
|
||||
this.setState({
|
||||
isChecked: !this.state.isChecked,
|
||||
});
|
||||
};
|
||||
onChangeCheckboxNotify = () => {
|
||||
this.setState({
|
||||
isNotify: !this.state.isNotify,
|
||||
});
|
||||
};
|
||||
onClickBackupList = () => {
|
||||
this.setState({
|
||||
isVisibleDialog: !this.state.isVisibleDialog,
|
||||
});
|
||||
};
|
||||
onModalClose = () => {
|
||||
this.setState({
|
||||
isVisibleDialog: false,
|
||||
});
|
||||
};
|
||||
onClickInput = () => {
|
||||
this.setState({
|
||||
isPanelVisible: true,
|
||||
});
|
||||
};
|
||||
onPanelClose = () => {
|
||||
this.setState({
|
||||
isPanelVisible: false,
|
||||
});
|
||||
};
|
||||
onClickShowStorage = (e) => {
|
||||
let newStateObj = {};
|
||||
const name = e.target.name;
|
||||
newStateObj[name] = true;
|
||||
const newState = this.switches.filter((el) => el !== name);
|
||||
newState.forEach((name) => (newStateObj[name] = false));
|
||||
this.setState({
|
||||
...newStateObj,
|
||||
});
|
||||
};
|
||||
onSelectFile = (file) => {
|
||||
this.setState({
|
||||
selectedFileId: file.id,
|
||||
});
|
||||
};
|
||||
onSelectLocalFile = (data) => {
|
||||
this.setState({
|
||||
selectedFile: data,
|
||||
});
|
||||
};
|
||||
canRestore = () => {
|
||||
const {
|
||||
isCheckedDocuments,
|
||||
isCheckedLocalFile,
|
||||
selectedFileId,
|
||||
selectedFile,
|
||||
isCheckedThirdPartyStorage,
|
||||
isCheckedThirdParty,
|
||||
} = this.state;
|
||||
|
||||
const { isFormReady } = this.props;
|
||||
|
||||
if (isCheckedDocuments || isCheckedThirdParty) {
|
||||
if (!selectedFileId) return false;
|
||||
return true;
|
||||
}
|
||||
if (isCheckedLocalFile) {
|
||||
if (!selectedFile) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isCheckedThirdPartyStorage) {
|
||||
return isFormReady();
|
||||
}
|
||||
};
|
||||
onRestoreClick = async () => {
|
||||
const {
|
||||
isNotify,
|
||||
isCheckedDocuments,
|
||||
isCheckedLocalFile,
|
||||
selectedFileId,
|
||||
selectedFile,
|
||||
isCheckedThirdPartyStorage,
|
||||
isCheckedThirdParty,
|
||||
} = this.state;
|
||||
const {
|
||||
history,
|
||||
socketHelper,
|
||||
getStorageParams,
|
||||
setTenantStatus,
|
||||
} = this.props;
|
||||
|
||||
if (!this.canRestore()) {
|
||||
this.setState({
|
||||
isFileSelectedError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
checkingRecoveryData: true,
|
||||
isFileSelectedError: false,
|
||||
});
|
||||
let storageParams = [];
|
||||
let obj = {};
|
||||
const backupId = "";
|
||||
const storageType = isCheckedDocuments
|
||||
? `${DocumentModuleType}`
|
||||
: isCheckedThirdParty
|
||||
? `${ResourcesModuleType}`
|
||||
: isCheckedLocalFile
|
||||
? `${LocalFileModuleType}`
|
||||
: `${StorageModuleType}`;
|
||||
|
||||
if (isCheckedThirdPartyStorage) {
|
||||
storageParams = getStorageParams(true, null, this.storageId);
|
||||
} else {
|
||||
obj.key = "filePath";
|
||||
if (isCheckedDocuments || isCheckedThirdParty) {
|
||||
obj.value = selectedFileId;
|
||||
} else {
|
||||
obj.value = "";
|
||||
}
|
||||
storageParams.push(obj);
|
||||
}
|
||||
let checkedFile;
|
||||
try {
|
||||
if (isCheckedLocalFile) {
|
||||
checkedFile = await request({
|
||||
baseURL: combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
config.homepage
|
||||
),
|
||||
method: "post",
|
||||
url: `/backupFileUpload.ashx`,
|
||||
responseType: "text",
|
||||
data: selectedFile,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
this.setState({
|
||||
checkingRecoveryData: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isCheckedLocalFile && checkedFile?.Message) {
|
||||
toastr.error(checkedFile.Message);
|
||||
this.setState({
|
||||
checkingRecoveryData: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
startRestore(backupId, storageType, storageParams, isNotify)
|
||||
.then(() => setTenantStatus(TenantStatus.PortalRestore))
|
||||
.then(() => {
|
||||
socketHelper.emit({
|
||||
command: "restore-backup",
|
||||
});
|
||||
})
|
||||
.then(() => this.setState({ checkingRecoveryData: false }))
|
||||
.then(() =>
|
||||
history.push(
|
||||
combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
config.homepage,
|
||||
"/preparation-portal"
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch((error) => {
|
||||
toastr.error(error);
|
||||
this.setState({
|
||||
checkingRecoveryData: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onSetStorageId = (storageId) => {
|
||||
this.storageId = storageId;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
history,
|
||||
downloadingProgress,
|
||||
buttonSize,
|
||||
theme,
|
||||
isEnableRestore,
|
||||
} = this.props;
|
||||
const {
|
||||
isChecked,
|
||||
isInitialLoading,
|
||||
isNotify,
|
||||
isVisibleDialog,
|
||||
isPanelVisible,
|
||||
isCheckedDocuments,
|
||||
isCheckedThirdParty,
|
||||
isCheckedThirdPartyStorage,
|
||||
isCheckedLocalFile,
|
||||
checkingRecoveryData,
|
||||
isFileSelectedError,
|
||||
} = this.state;
|
||||
|
||||
const commonRadioButtonProps = {
|
||||
fontSize: "13px",
|
||||
fontWeight: "400",
|
||||
value: "value",
|
||||
className: "backup_radio-button",
|
||||
onClick: this.onClickShowStorage,
|
||||
return () => {
|
||||
clearProgressInterval();
|
||||
setRestoreResource(null);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onClickVersionListProp = isEnableRestore
|
||||
? { onClick: this.onClickBackupList }
|
||||
: {};
|
||||
const onChangeRadioButton = (e) => {
|
||||
const value = e.target.value;
|
||||
if (value === radioButtonState) return;
|
||||
|
||||
const isMaxProgress = downloadingProgress === 100;
|
||||
setRestoreResource(null);
|
||||
setRadioButtonState(value);
|
||||
};
|
||||
|
||||
return isInitialLoading ? (
|
||||
<RestoreBackupLoader />
|
||||
) : (
|
||||
<StyledRestoreBackup theme={theme} isEnableRestore={isEnableRestore}>
|
||||
<div className="restore-description">
|
||||
<Text className="restore-description settings_unavailable">
|
||||
{t("RestoreBackupDescription")}
|
||||
</Text>
|
||||
</div>
|
||||
const onChangeCheckbox = (e) => {
|
||||
const name = e.target.name;
|
||||
const checked = e.target.checked;
|
||||
|
||||
<RadioButton
|
||||
label={t("LocalFile")}
|
||||
name={"isCheckedLocalFile"}
|
||||
key={4}
|
||||
isChecked={isCheckedLocalFile}
|
||||
setCheckboxState({ ...checkboxState, [name]: checked });
|
||||
};
|
||||
|
||||
const getStorageType = () => {
|
||||
switch (radioButtonState) {
|
||||
case LOCAL_FILE:
|
||||
return LocalFileModuleType;
|
||||
case BACKUP_ROOM:
|
||||
return DocumentModuleType;
|
||||
case DISK_SPACE:
|
||||
return ResourcesModuleType;
|
||||
case STORAGE_SPACE:
|
||||
return StorageModuleType;
|
||||
}
|
||||
};
|
||||
|
||||
const onClickBackupList = () => {
|
||||
setIsVisibleBackupListDialog(true);
|
||||
};
|
||||
|
||||
const onClickInput = () => {
|
||||
setIsVisibleSelectFileDialog(true);
|
||||
};
|
||||
const onModalClose = () => {
|
||||
setIsVisibleBackupListDialog(false);
|
||||
setIsVisibleSelectFileDialog(false);
|
||||
};
|
||||
|
||||
const onSetStorageId = (id) => {
|
||||
setRestoreResource(id);
|
||||
};
|
||||
|
||||
const radioButtonContent = (
|
||||
<>
|
||||
<RadioButtonGroup
|
||||
name="restore_backup"
|
||||
orientation="vertical"
|
||||
fontSize="13px"
|
||||
fontWeight="400"
|
||||
className="backup_radio-button"
|
||||
options={[
|
||||
{ value: LOCAL_FILE, label: t("LocalFile") },
|
||||
{ value: BACKUP_ROOM, label: t("RoomsModule") },
|
||||
{ value: DISK_SPACE, label: t("ThirdPartyResource") },
|
||||
{ value: STORAGE_SPACE, label: t("ThirdPartyStorage") },
|
||||
]}
|
||||
onClick={onChangeRadioButton}
|
||||
selected={radioButtonState}
|
||||
spacing="16px"
|
||||
isDisabled={!isEnableRestore}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
const backupModules = (
|
||||
<div className="restore-backup_modules">
|
||||
{radioButtonState === LOCAL_FILE && <LocalFileModule t={t} />}
|
||||
|
||||
{radioButtonState === BACKUP_ROOM && (
|
||||
<RoomsModule
|
||||
isDisabled={!isEnableRestore}
|
||||
{...commonRadioButtonProps}
|
||||
t={t}
|
||||
isPanelVisible={isVisibleSelectFileDialog}
|
||||
onClose={onModalClose}
|
||||
onClickInput={onClickInput}
|
||||
onSelectFile={(file) => setRestoreResource(file.id)}
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
label={t("RoomsModule")}
|
||||
name={"isCheckedDocuments"}
|
||||
key={1}
|
||||
isChecked={isCheckedDocuments}
|
||||
isDisabled={!isEnableRestore}
|
||||
{...commonRadioButtonProps}
|
||||
)}
|
||||
{radioButtonState === DISK_SPACE && (
|
||||
<ThirdPartyResourcesModule
|
||||
t={t}
|
||||
isPanelVisible={isVisibleSelectFileDialog}
|
||||
onClose={onModalClose}
|
||||
onClickInput={onClickInput}
|
||||
onSelectFile={(file) => setRestoreResource(file.id)}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
)}
|
||||
{radioButtonState === STORAGE_SPACE && (
|
||||
<ThirdPartyStoragesModule onSetStorageId={onSetStorageId} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
<RadioButton
|
||||
label={t("ThirdPartyResource")}
|
||||
name={"isCheckedThirdParty"}
|
||||
key={2}
|
||||
isChecked={isCheckedThirdParty}
|
||||
isDisabled={!isEnableRestore}
|
||||
{...commonRadioButtonProps}
|
||||
/>
|
||||
const warningContent = (
|
||||
<>
|
||||
<Text className="restore-backup_warning settings_unavailable" noSelect>
|
||||
{t("Common:Warning")}
|
||||
{"!"}
|
||||
</Text>
|
||||
<Text
|
||||
className="restore-backup_warning-description settings_unavailable"
|
||||
noSelect
|
||||
>
|
||||
{t("RestoreBackupWarningText")}
|
||||
</Text>
|
||||
<Text
|
||||
className="restore-backup_warning-link settings_unavailable"
|
||||
noSelect
|
||||
>
|
||||
{t("RestoreBackupResetInfoWarningText")}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
<RadioButton
|
||||
label={t("ThirdPartyStorage")}
|
||||
name={"isCheckedThirdPartyStorage"}
|
||||
key={3}
|
||||
isChecked={isCheckedThirdPartyStorage}
|
||||
isDisabled={!isEnableRestore}
|
||||
{...commonRadioButtonProps}
|
||||
/>
|
||||
const onClickVersionListProp = isEnableRestore
|
||||
? { onClick: onClickBackupList }
|
||||
: {};
|
||||
|
||||
<div className="restore-backup_modules">
|
||||
{isCheckedDocuments && (
|
||||
<RoomsModule
|
||||
isDisabled={!isEnableRestore}
|
||||
t={t}
|
||||
isPanelVisible={isPanelVisible}
|
||||
onClose={this.onPanelClose}
|
||||
onClickInput={this.onClickInput}
|
||||
onSelectFile={this.onSelectFile}
|
||||
isError={isFileSelectedError}
|
||||
/>
|
||||
)}
|
||||
{isCheckedThirdParty && (
|
||||
<ThirdPartyResources
|
||||
t={t}
|
||||
isPanelVisible={isPanelVisible}
|
||||
onClose={this.onPanelClose}
|
||||
onClickInput={this.onClickInput}
|
||||
onSelectFile={this.onSelectFile}
|
||||
isError={isFileSelectedError}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
)}
|
||||
{isCheckedThirdPartyStorage && (
|
||||
<ThirdPartyStorages
|
||||
onResetFormSettings={this.onResetFormSettings}
|
||||
onSetStorageId={this.onSetStorageId}
|
||||
/>
|
||||
)}
|
||||
{isCheckedLocalFile && (
|
||||
<LocalFile
|
||||
hasError={isFileSelectedError}
|
||||
onSelectLocalFile={this.onSelectLocalFile}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Text
|
||||
className="restore-backup_list settings_unavailable"
|
||||
{...onClickVersionListProp}
|
||||
noSelect
|
||||
>
|
||||
{t("BackupList")}
|
||||
if (isInitialLoading) return <RestoreBackupLoader />;
|
||||
console.log("index render");
|
||||
return (
|
||||
<StyledRestoreBackup isEnableRestore={isEnableRestore}>
|
||||
<div className="restore-description">
|
||||
<Text className="restore-description settings_unavailable">
|
||||
{t("RestoreBackupDescription")}
|
||||
</Text>
|
||||
</div>
|
||||
{radioButtonContent}
|
||||
{backupModules}
|
||||
|
||||
{isVisibleDialog && (
|
||||
<BackupListModalDialog
|
||||
isVisibleDialog={isVisibleDialog}
|
||||
onModalClose={this.onModalClose}
|
||||
isNotify={isNotify}
|
||||
isCopyingToLocal={!isMaxProgress}
|
||||
history={history}
|
||||
/>
|
||||
)}
|
||||
<Checkbox
|
||||
truncate
|
||||
className="restore-backup-checkbox_notification"
|
||||
onChange={this.onChangeCheckboxNotify}
|
||||
isChecked={isNotify}
|
||||
label={t("SendNotificationAboutRestoring")}
|
||||
isDisabled={!isEnableRestore}
|
||||
<Text
|
||||
className="restore-backup_list settings_unavailable"
|
||||
{...onClickVersionListProp}
|
||||
noSelect
|
||||
>
|
||||
{t("BackupList")}
|
||||
</Text>
|
||||
|
||||
{isVisibleBackupListDialog && (
|
||||
<BackupListModalDialog
|
||||
isVisibleDialog={isVisibleBackupListDialog}
|
||||
onModalClose={onModalClose}
|
||||
isNotify={checkboxState.notification}
|
||||
history={history}
|
||||
/>
|
||||
|
||||
<Text className="restore-backup_warning settings_unavailable" noSelect>
|
||||
{t("Common:Warning")}
|
||||
{"!"}
|
||||
</Text>
|
||||
<Text
|
||||
className="restore-backup_warning-description settings_unavailable"
|
||||
noSelect
|
||||
>
|
||||
{t("RestoreBackupWarningText")}
|
||||
</Text>
|
||||
<Text
|
||||
className="restore-backup_warning-link settings_unavailable"
|
||||
noSelect
|
||||
>
|
||||
{t("RestoreBackupResetInfoWarningText")}
|
||||
</Text>
|
||||
|
||||
<Checkbox
|
||||
truncate
|
||||
className="restore-backup-checkbox"
|
||||
onChange={this.onChangeCheckbox}
|
||||
isChecked={isChecked}
|
||||
label={t("UserAgreement")}
|
||||
isDisabled={!isEnableRestore}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="restore-backup_button"
|
||||
label={t("Common:Restore")}
|
||||
onClick={this.onRestoreClick}
|
||||
primary
|
||||
isDisabled={
|
||||
checkingRecoveryData ||
|
||||
!isMaxProgress ||
|
||||
!isChecked ||
|
||||
!isEnableRestore
|
||||
}
|
||||
isLoading={checkingRecoveryData}
|
||||
size={buttonSize}
|
||||
tabIndex={10}
|
||||
/>
|
||||
|
||||
{downloadingProgress > 0 && downloadingProgress !== 100 && (
|
||||
<FloatingButton
|
||||
className="layout-progress-bar"
|
||||
icon="file"
|
||||
alert={false}
|
||||
percent={downloadingProgress}
|
||||
/>
|
||||
)}
|
||||
</StyledRestoreBackup>
|
||||
);
|
||||
}
|
||||
}
|
||||
)}
|
||||
<Checkbox
|
||||
truncate
|
||||
name={NOTIFICATION}
|
||||
className="restore-backup-checkbox_notification"
|
||||
onChange={onChangeCheckbox}
|
||||
isChecked={checkboxState.notification}
|
||||
label={t("SendNotificationAboutRestoring")}
|
||||
isDisabled={!isEnableRestore}
|
||||
/>
|
||||
{warningContent}
|
||||
<Checkbox
|
||||
truncate
|
||||
name={CONFIRMATION}
|
||||
className="restore-backup-checkbox"
|
||||
onChange={onChangeCheckbox}
|
||||
isChecked={checkboxState.confirmation}
|
||||
label={t("UserAgreement")}
|
||||
isDisabled={!isEnableRestore}
|
||||
/>
|
||||
<ButtonContainer
|
||||
isConfirmed={checkboxState.confirmation}
|
||||
isNotification={checkboxState.notification}
|
||||
getStorageType={getStorageType}
|
||||
radioButtonState={radioButtonState}
|
||||
isCheckedThirdPartyStorage={radioButtonState === STORAGE_SPACE}
|
||||
isCheckedLocalFile={radioButtonState === LOCAL_FILE}
|
||||
history={history}
|
||||
t={t}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
</StyledRestoreBackup>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, backup }) => {
|
||||
const { settingsStore, currentQuotaStore } = auth;
|
||||
const { socketHelper, theme, isTabletView, setTenantStatus } = settingsStore;
|
||||
const { isTabletView } = settingsStore;
|
||||
const { isRestoreAndAutoBackupAvailable } = currentQuotaStore;
|
||||
const {
|
||||
downloadingProgress,
|
||||
getProgress,
|
||||
clearProgressInterval,
|
||||
setStorageRegions,
|
||||
setThirdPartyStorage,
|
||||
isFormReady,
|
||||
getStorageParams,
|
||||
setConnectedThirdPartyAccount,
|
||||
setRestoreResource,
|
||||
} = backup;
|
||||
|
||||
const buttonSize = isTabletView ? "normal" : "small";
|
||||
const { isRestoreAndAutoBackupAvailable } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
setTenantStatus,
|
||||
isEnableRestore: isRestoreAndAutoBackupAvailable,
|
||||
setStorageRegions,
|
||||
setThirdPartyStorage,
|
||||
buttonSize,
|
||||
setConnectedThirdPartyAccount,
|
||||
theme,
|
||||
clearProgressInterval,
|
||||
downloadingProgress,
|
||||
socketHelper,
|
||||
isFormReady,
|
||||
|
||||
getProgress,
|
||||
getStorageParams,
|
||||
setRestoreResource,
|
||||
};
|
||||
})(withTranslation(["Settings", "Common"])(observer(RestoreBackup)));
|
||||
|
@ -0,0 +1,171 @@
|
||||
import React, { useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import config from "PACKAGE_FILE";
|
||||
|
||||
import Button from "@docspace/components/button";
|
||||
import FloatingButton from "@docspace/common/components/FloatingButton";
|
||||
import { TenantStatus } from "@docspace/common/constants";
|
||||
import { startRestore } from "@docspace/common/api/portal";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { request } from "@docspace/common/api/client";
|
||||
|
||||
const ButtonContainer = (props) => {
|
||||
const {
|
||||
downloadingProgress,
|
||||
isMaxProgress,
|
||||
isConfirmed,
|
||||
getStorageType,
|
||||
isNotification,
|
||||
restoreResource,
|
||||
isCheckedThirdPartyStorage,
|
||||
isCheckedLocalFile,
|
||||
history,
|
||||
isEnableRestore,
|
||||
t,
|
||||
buttonSize,
|
||||
socketHelper,
|
||||
setTenantStatus,
|
||||
isFormReady,
|
||||
getStorageParams,
|
||||
} = props;
|
||||
|
||||
const [isUploadingLocalFile, setIsUploadingLocalFile] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const localFileUploading = async () => {
|
||||
try {
|
||||
const checkedFile = await request({
|
||||
baseURL: combineUrl(window.DocSpaceConfig?.proxy?.url, config.homepage),
|
||||
method: "post",
|
||||
url: `/backupFileUpload.ashx`,
|
||||
responseType: "text",
|
||||
data: restoreResource,
|
||||
});
|
||||
|
||||
return checkedFile;
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
setIsUploadingLocalFile(false);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const onRestoreClick = async () => {
|
||||
if (isCheckedThirdPartyStorage) {
|
||||
const requiredFieldsFilled = isFormReady();
|
||||
if (!requiredFieldsFilled) return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
|
||||
let storageParams = [],
|
||||
tempObj = {};
|
||||
|
||||
const backupId = "";
|
||||
const storageType = getStorageType().toString();
|
||||
|
||||
if (isCheckedThirdPartyStorage) {
|
||||
storageParams = getStorageParams(true, null, restoreResource);
|
||||
} else {
|
||||
tempObj.key = "filePath";
|
||||
tempObj.value = isCheckedLocalFile ? "" : restoreResource;
|
||||
|
||||
storageParams.push(tempObj);
|
||||
}
|
||||
|
||||
if (isCheckedLocalFile) {
|
||||
const isUploadedFile = await localFileUploading();
|
||||
|
||||
if (!isUploadedFile) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUploadedFile?.Message) {
|
||||
toastr.error(isUploadedFile.Message);
|
||||
setIsUploadingLocalFile(false);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await startRestore(backupId, storageType, storageParams, isNotification);
|
||||
setTenantStatus(TenantStatus.PortalRestore);
|
||||
|
||||
socketHelper.emit({
|
||||
command: "restore-backup",
|
||||
});
|
||||
|
||||
history.push(
|
||||
combineUrl(
|
||||
window.DocSpaceConfig?.proxy?.url,
|
||||
config.homepage,
|
||||
"/preparation-portal"
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
|
||||
setIsUploadingLocalFile(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const isButtonDisabled =
|
||||
isLoading ||
|
||||
isUploadingLocalFile ||
|
||||
!isMaxProgress ||
|
||||
!isConfirmed ||
|
||||
!isEnableRestore ||
|
||||
!restoreResource;
|
||||
const isLoadingButton = isUploadingLocalFile || isLoading;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className="restore-backup_button"
|
||||
label={t("Common:Restore")}
|
||||
onClick={onRestoreClick}
|
||||
primary
|
||||
isDisabled={isButtonDisabled}
|
||||
isLoading={isLoadingButton}
|
||||
size={buttonSize}
|
||||
tabIndex={10}
|
||||
/>
|
||||
|
||||
{downloadingProgress > 0 && !isMaxProgress && (
|
||||
<FloatingButton
|
||||
className="layout-progress-bar"
|
||||
icon="file"
|
||||
alert={false}
|
||||
percent={downloadingProgress}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, backup }) => {
|
||||
const { settingsStore, currentQuotaStore } = auth;
|
||||
const { socketHelper, setTenantStatus } = settingsStore;
|
||||
const {
|
||||
downloadingProgress,
|
||||
isFormReady,
|
||||
getStorageParams,
|
||||
restoreResource,
|
||||
} = backup;
|
||||
|
||||
const { isRestoreAndAutoBackupAvailable } = currentQuotaStore;
|
||||
const isMaxProgress = downloadingProgress === 100;
|
||||
return {
|
||||
isMaxProgress,
|
||||
setTenantStatus,
|
||||
isEnableRestore: isRestoreAndAutoBackupAvailable,
|
||||
downloadingProgress,
|
||||
socketHelper,
|
||||
isFormReady,
|
||||
getStorageParams,
|
||||
restoreResource,
|
||||
};
|
||||
})(observer(ButtonContainer));
|
@ -1,27 +1,33 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import FileInput from "@docspace/components/file-input";
|
||||
|
||||
const LocalFile = ({ onSelectLocalFile, hasError }) => {
|
||||
const LocalFile = ({ setRestoreResource, isEnableRestore, t }) => {
|
||||
const onClickInput = (file) => {
|
||||
let data = new FormData();
|
||||
data.append("file", file);
|
||||
|
||||
onSelectLocalFile(data);
|
||||
setRestoreResource(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
onSelectLocalFile("");
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<FileInput
|
||||
hasError={hasError}
|
||||
onInput={onClickInput}
|
||||
scale
|
||||
className="restore-backup_input"
|
||||
isDisabled={!isEnableRestore}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocalFile;
|
||||
export default inject(({ auth, backup }) => {
|
||||
const { currentQuotaStore } = auth;
|
||||
const { setRestoreResource } = backup;
|
||||
const { isRestoreAndAutoBackupAvailable } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
isEnableRestore: isRestoreAndAutoBackupAvailable,
|
||||
setRestoreResource,
|
||||
};
|
||||
})(observer(LocalFile));
|
||||
|
@ -302,14 +302,17 @@ BackupListModalDialog.propTypes = {
|
||||
isVisibleDialog: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
export default inject(({ auth, backup }) => {
|
||||
const { settingsStore } = auth;
|
||||
const { downloadingProgress } = backup;
|
||||
const { socketHelper, theme, setTenantStatus } = settingsStore;
|
||||
const isCopyingToLocal = downloadingProgress !== 100;
|
||||
|
||||
return {
|
||||
setTenantStatus,
|
||||
theme,
|
||||
socketHelper,
|
||||
isCopyingToLocal,
|
||||
};
|
||||
})(
|
||||
withTranslation(["Settings", "Common", "Translations"])(
|
||||
|
@ -12,6 +12,8 @@ import { AutoBackupPeriod } from "@docspace/common/constants";
|
||||
const { EveryDayType, EveryWeekType } = AutoBackupPeriod;
|
||||
|
||||
class BackupStore {
|
||||
restoreResource = null;
|
||||
|
||||
backupSchedule = {};
|
||||
backupStorage = {};
|
||||
|
||||
@ -343,7 +345,8 @@ class BackupStore {
|
||||
};
|
||||
|
||||
clearLocalStorage = () => {
|
||||
removeLocalStorage("LocalCopyStorageType");
|
||||
getFromLocalStorage("LocalCopyStorageType") &&
|
||||
removeLocalStorage("LocalCopyStorageType");
|
||||
|
||||
getFromLocalStorage("LocalCopyFolder") &&
|
||||
removeLocalStorage("LocalCopyFolder");
|
||||
@ -610,6 +613,10 @@ class BackupStore {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
setRestoreResource = (value) => {
|
||||
this.restoreResource = value;
|
||||
};
|
||||
}
|
||||
|
||||
export default BackupStore;
|
||||
|
@ -27,7 +27,6 @@ function MediaViewer({
|
||||
...props
|
||||
}: MediaViewerProps): JSX.Element {
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [canSwipeImage, setCanSwipeImage] = useState<boolean>(true);
|
||||
const [fileUrl, setFileUrl] = useState<string>(() => {
|
||||
const { playlist, currentFileId } = props;
|
||||
const item = playlist.find(
|
||||
@ -79,12 +78,6 @@ function MediaViewer({
|
||||
if (ext === ".tiff" || ext === ".tif") {
|
||||
fetchAndSetTiffDataURL(src);
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKeydown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@ -122,6 +115,19 @@ function MediaViewer({
|
||||
playlistPos,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKeydown);
|
||||
};
|
||||
}, [
|
||||
props.playlist.length,
|
||||
props.files.length,
|
||||
playlistPos,
|
||||
props.deleteDialogVisible,
|
||||
]);
|
||||
|
||||
const getContextModel = () => {
|
||||
const {
|
||||
t,
|
||||
@ -273,7 +279,6 @@ function MediaViewer({
|
||||
|
||||
if (!canDelete) return;
|
||||
|
||||
setCanSwipeImage(false);
|
||||
if (!isNullOrUndefined(currentFileId)) onDelete(currentFileId);
|
||||
};
|
||||
|
||||
@ -292,10 +297,11 @@ function MediaViewer({
|
||||
if (code in KeyboardEventKeys) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (props.deleteDialogVisible) return;
|
||||
|
||||
switch (code) {
|
||||
case KeyboardEventKeys.ArrowLeft:
|
||||
if (document.fullscreenElement || !canSwipeImage) return;
|
||||
if (document.fullscreenElement) return;
|
||||
|
||||
if (ctrlKey) {
|
||||
const rotateLeftElement = document.getElementsByClassName(
|
||||
@ -309,7 +315,7 @@ function MediaViewer({
|
||||
break;
|
||||
|
||||
case KeyboardEventKeys.ArrowRight:
|
||||
if (document.fullscreenElement || !canSwipeImage) return;
|
||||
if (document.fullscreenElement) return;
|
||||
|
||||
if (ctrlKey) {
|
||||
const rotateRightElement = document.getElementsByClassName(
|
||||
|
@ -138,6 +138,7 @@ class SettingsStore {
|
||||
companyInfoSettingsIsDefault = true;
|
||||
|
||||
whiteLabelLogoUrls = [];
|
||||
standalone = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "IP ünvan kimi domen dəstəklənmir",
|
||||
"Done": "İcra edildi",
|
||||
"Download": "Endirin",
|
||||
"Duplicate": "Sürətini yaratmaq",
|
||||
"CreateCopy": "Sürətini yaratmaq",
|
||||
"EditButton": "Redaktə edin",
|
||||
"Email": "E-poçt",
|
||||
"EmptyEmail": "Elektron poçt sahəsi boşdur",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Домейни като IP адрес не се поддържат",
|
||||
"Done": "Готово",
|
||||
"Download": "Изтегли",
|
||||
"Duplicate": "Създай копие",
|
||||
"CreateCopy": "Създай копие",
|
||||
"EditButton": "Редактирай",
|
||||
"Email": "Имейл",
|
||||
"EmptyEmail": "Никой имейл не е анализиран",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domény jako IP adresa nejsou podporovány",
|
||||
"Done": "Hotovo",
|
||||
"Download": "Stáhnout",
|
||||
"Duplicate": "Vytvořit kopii",
|
||||
"CreateCopy": "Vytvořit kopii",
|
||||
"EditButton": "Upravit",
|
||||
"Email": "E-mail",
|
||||
"EmptyEmail": "Nebyl analyzován žádný e-mail",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domains als IP-Adressen werden nicht unterstützt",
|
||||
"Done": "Fertig",
|
||||
"Download": "Herunterladen",
|
||||
"Duplicate": "Kopie erstellen",
|
||||
"CreateCopy": "Kopie erstellen",
|
||||
"EditButton": "Bearbeiten",
|
||||
"Email": "E-Mail",
|
||||
"EmptyEmail": "Kein E-Mail wurde eingegeben",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Οι τομείς ως διεύθυνση IP δεν υποστηρίζονται",
|
||||
"Done": "Τέλος",
|
||||
"Download": "Λήψη",
|
||||
"Duplicate": "Δημιουργήστε ένα αντίγραφο",
|
||||
"CreateCopy": "Δημιουργήστε ένα αντίγραφο",
|
||||
"EditButton": "Επεξεργασία",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Δεν αναλύθηκε email",
|
||||
|
@ -79,7 +79,8 @@
|
||||
"Done": "Done",
|
||||
"DontAskAgain": "Don't ask file name again on creation",
|
||||
"Download": "Download",
|
||||
"Duplicate": "Create copy",
|
||||
"Duplicate": "Duplicate",
|
||||
"CreateCopy": "Create copy",
|
||||
"EditButton": "Edit",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "No email address parsed",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "No se admiten dominios como dirección IP",
|
||||
"Done": "Listo",
|
||||
"Download": "Descargar",
|
||||
"Duplicate": "Crear una copia",
|
||||
"CreateCopy": "Crear una copia",
|
||||
"EditButton": "Editar",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "El campo de email está vacío",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Verkkotunnuksia IP-osoitteina ei tueta",
|
||||
"Done": "Tehty",
|
||||
"Download": "Lataa",
|
||||
"Duplicate": "Luo kopio",
|
||||
"CreateCopy": "Luo kopio",
|
||||
"EditButton": "Muokkaa",
|
||||
"Email": "Sähköposti",
|
||||
"EmptyEmail": "Yhtään sähköpostia ei ole jäsennelty",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Les domaines sous forme d'adresse IP ne sont pas pris en charge",
|
||||
"Done": "Terminé",
|
||||
"Download": "Télécharger",
|
||||
"Duplicate": "Créer une copie",
|
||||
"CreateCopy": "Créer une copie",
|
||||
"EditButton": "Modifier",
|
||||
"Email": "E-mail",
|
||||
"EmptyEmail": "Aucun e-mail n'a été analysé",
|
||||
|
@ -65,7 +65,7 @@
|
||||
"DomainIpAddress": "Տիրույթները որպես IP հասցե չեն աջակցվում",
|
||||
"Done": "Ավարտված է",
|
||||
"Download": "Ներբեռնել",
|
||||
"Duplicate": "Ստեղծել պատճեն",
|
||||
"CreateCopy": "Ստեղծել պատճեն",
|
||||
"EditButton": "Խմբագրել",
|
||||
"Email": "Էլ-փոստ",
|
||||
"EmptyEmail": "Էլ․փոստ չի վերլուծվել",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "I domini come indirizzo IP non sono supportati",
|
||||
"Done": "Fine",
|
||||
"Download": "Scarica",
|
||||
"Duplicate": "Crea una copia",
|
||||
"CreateCopy": "Crea una copia",
|
||||
"EditButton": "Modifica",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Nessuna email analizzata",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "IPアドレスとしてのドメインには対応していません",
|
||||
"Done": "完了",
|
||||
"Download": "ダウンロード",
|
||||
"Duplicate": "コピーの作成",
|
||||
"CreateCopy": "コピーの作成",
|
||||
"EditButton": "編集",
|
||||
"Email": "メール",
|
||||
"EmptyEmail": "誰もメールを解析しない",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "지원되지 않는 도메인 IP 주소입니다",
|
||||
"Done": "완료",
|
||||
"Download": "다운로드",
|
||||
"Duplicate": "복사본 생성",
|
||||
"CreateCopy": "복사본 생성",
|
||||
"EditButton": "편집",
|
||||
"Email": "이메일",
|
||||
"EmptyEmail": "파싱된 이메일이 하나도 없습니다",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "ທີ່ຢູ່ IP ໂດເມນຍັງບໍ່ຮອງຮັບ",
|
||||
"Done": "ສໍາເລັດ",
|
||||
"Download": "ດາວໂຫຼດ",
|
||||
"Duplicate": "ສ້າງສຳເນົາ",
|
||||
"CreateCopy": "ສ້າງສຳເນົາ",
|
||||
"EditButton": "ແກ້ໄຂ",
|
||||
"Email": "ອີເມວ",
|
||||
"EmptyEmail": "ບໍ່ແບ່ງແຍກອີເມວ",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domēni kā IP adrese netiek atbalstīti",
|
||||
"Done": "Gatavs",
|
||||
"Download": "Lejupielādēt",
|
||||
"Duplicate": "Izveidojiet kopiju",
|
||||
"CreateCopy": "Izveidojiet kopiju",
|
||||
"EditButton": "Rediģēt",
|
||||
"Email": "E-pasts",
|
||||
"EmptyEmail": "Neviens e-pasts nav parsēts",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domeinen als IP-adres worden niet ondersteund",
|
||||
"Done": "Gereed",
|
||||
"Download": "Download",
|
||||
"Duplicate": "Maak een kopie",
|
||||
"CreateCopy": "Maak een kopie",
|
||||
"EditButton": "Bewerk",
|
||||
"Email": "E-mail",
|
||||
"EmptyEmail": "Geen enkele e-mail verwerkt",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domena i adres IP nie są obsługiwane",
|
||||
"Done": "Gotowe",
|
||||
"Download": "Pobierz",
|
||||
"Duplicate": "Utwórz kopię",
|
||||
"CreateCopy": "Utwórz kopię",
|
||||
"EditButton": "Edytuj",
|
||||
"Email": "E-mail",
|
||||
"EmptyEmail": "Adres e-mail nie może być pusty",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Os domínios como endereço IP não são suportados",
|
||||
"Done": "Concluído",
|
||||
"Download": "Baixar",
|
||||
"Duplicate": "Criar uma cópia",
|
||||
"CreateCopy": "Criar uma cópia",
|
||||
"EditButton": "Editar",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Ninguém analisou o e-mail",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Os domínios como endereço IP não são suportados",
|
||||
"Done": "Concluído",
|
||||
"Download": "Download",
|
||||
"Duplicate": "Criar uma cópia",
|
||||
"CreateCopy": "Criar uma cópia",
|
||||
"EditButton": "Editar",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Sem análise de e-mail",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domenii sub forma unei adrese IP nu sunt acceptate",
|
||||
"Done": "Gata",
|
||||
"Download": "Descărcare",
|
||||
"Duplicate": "Crează o copie",
|
||||
"CreateCopy": "Crează o copie",
|
||||
"EditButton": "Editare",
|
||||
"Email": "e-mail",
|
||||
"EmptyEmail": "Niciun e-mail nu a fost parsat",
|
||||
|
@ -79,7 +79,8 @@
|
||||
"Done": "Успешно",
|
||||
"DontAskAgain": "Больше не запрашивать имя файла при создании",
|
||||
"Download": "Скачать",
|
||||
"Duplicate": "Создать копию",
|
||||
"Duplicate": "Дублировать",
|
||||
"CreateCopy": "Создать копию",
|
||||
"EditButton": "Редактировать",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Поле email пустое",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domény ako adresa IP nie sú podporované",
|
||||
"Done": "Hotovo",
|
||||
"Download": "Stiahnuť ",
|
||||
"Duplicate": "Vytvoriť kópiu",
|
||||
"CreateCopy": "Vytvoriť kópiu",
|
||||
"EditButton": "Upraviť",
|
||||
"Email": "E-mail",
|
||||
"EmptyEmail": "Žiadny e-mail nebol analyzovaný",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Domene kot IP naslov niso podprte",
|
||||
"Done": "Končano",
|
||||
"Download": "Prenesi",
|
||||
"Duplicate": "Ustvari kopijo",
|
||||
"CreateCopy": "Ustvari kopijo",
|
||||
"EditButton": "Uredi",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Noben e-mail ni razčlenjen",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "IP adresi olarak alan adları desteklenmez",
|
||||
"Done": "Tamam",
|
||||
"Download": "İndir",
|
||||
"Duplicate": "Bir kopya oluştur",
|
||||
"CreateCopy": "Bir kopya oluştur",
|
||||
"EditButton": "Düzenle",
|
||||
"Email": "E-posta",
|
||||
"EmptyEmail": "Hiçbir e-posta ayrıştırılmadı",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Домени в якості IP-адрес не підтримуються",
|
||||
"Done": "Готово",
|
||||
"Download": "Завантажити",
|
||||
"Duplicate": "Створити копію",
|
||||
"CreateCopy": "Створити копію",
|
||||
"EditButton": "Редагувати",
|
||||
"Email": "Електронна пошта",
|
||||
"EmptyEmail": "Не проаналізовано жодної адреси електронної пошти",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "Các tên miền dưới dạng địa chỉ IP không được hỗ trợ",
|
||||
"Done": "Đã xong",
|
||||
"Download": "Tải xuống",
|
||||
"Duplicate": "Tạo bản sao",
|
||||
"CreateCopy": "Tạo bản sao",
|
||||
"EditButton": "Chỉnh sửa",
|
||||
"Email": "Email",
|
||||
"EmptyEmail": "Không có email nào được phân tích",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"DomainIpAddress": "不支持使用域作为IP地址",
|
||||
"Done": "完成",
|
||||
"Download": "下载",
|
||||
"Duplicate": "创建副本",
|
||||
"CreateCopy": "创建副本",
|
||||
"EditButton": "编辑",
|
||||
"Email": "电子邮件",
|
||||
"EmptyEmail": "没有解析邮件",
|
||||
|
Loading…
Reference in New Issue
Block a user