Skip to content

Commit

Permalink
Merge pull request 'feature/convert-with-password' (#97) from feature…
Browse files Browse the repository at this point in the history
…/convert-with-password into develop

Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/DocSpace-client/pulls/97
Reviewed-by: Alexey Safronov <alexey.safronov@onlyoffice.com>
  • Loading branch information
AlexeySafronov committed Dec 16, 2024
2 parents 7ceaeb4 + 597b0c7 commit 7a2cc02
Show file tree
Hide file tree
Showing 18 changed files with 1,078 additions and 154 deletions.
8 changes: 7 additions & 1 deletion packages/client/public/locales/en/DownloadDialog.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
{
"AccessPasswordMessage": "This file is protected, please enter the password to access it or download in original format.",
"ChooseFormatText": "Choose the format for each file to be downloaded",
"ConvertMessage": "If you choose to convert the file to a format different from the original, some data might be lost.",
"ConvertToZip": "Files will be compressed into the <strong>.zip file</strong>",
"CustomFormat": "Custom format",
"OriginalFormat": "Original format"
"DownloadOriginalFormat": "In original format",
"FileProtectionMessage": "These files are protected. Please try entering the password for all files. If you don't remember the password but need these files, you can download them in their original format or simply remove them from the list.",
"OriginalFormat": "Original format",
"ProtectedFiles": "Protected files",
"PasswordEntered": "Password entered",
"RemovedFromList": "Removed from list"
}
17 changes: 6 additions & 11 deletions packages/client/src/components/SimulatePassword/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,15 @@ const bulletsFont = "•";
const StyledBody = styled.div`
width: 100%;
#conversion-password {
.conversion-input {
width: 100%;
text-align: start;
max-width: ${(props) =>
props.inputMaxWidth ? props.inputMaxWidth : "382px"};
width: 100%;
margin: 0;
}
.conversion-input {
width: 100%;
text-align: start;
}
`;
const SimulatePassword = memo(
({
Expand All @@ -72,9 +71,7 @@ const SimulatePassword = memo(

const oldPassword = password;
const oldPasswordLength = oldPassword.length;
const caretPosition = document.getElementById(
"conversion-password",
).selectionStart;
const caretPosition = forwardedRef.current.selectionStart;

setCaretPosition(caretPosition);
const newCharactersUntilCaret = newPassword.substring(0, caretPosition);
Expand Down Expand Up @@ -134,9 +131,7 @@ const SimulatePassword = memo(

caretPosition &&
inputType === "password" &&
document
.getElementById("conversion-password")
.setSelectionRange(caretPosition, caretPosition);
forwardedRef.current?.setSelectionRange(caretPosition, caretPosition);
}, [password]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const DownloadContent = (props) => {
isChecked,
isIndeterminate,
theme,
getItemIcon,
} = props;

const getTitleExtensions = () => {
Expand Down Expand Up @@ -192,6 +193,7 @@ const DownloadContent = (props) => {
type={type}
isOther={isOther}
dropdownItems={dropdownItems}
getItemIcon={getItemIcon}
/>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

import React, { useState } from "react";
import { inject, observer } from "mobx-react";
import { ReactSVG } from "react-svg";

import { LinkWithDropdown } from "@docspace/shared/components/link-with-dropdown";
import { Text } from "@docspace/shared/components/text";
import { Checkbox } from "@docspace/shared/components/checkbox";
Expand All @@ -39,31 +39,15 @@ const DownloadRow = (props) => {
onRowSelect,
type,
dropdownItems,
getIcon,
getFolderIcon,
isOther,
isChecked,
getItemIcon,
} = props;

//console.log("DownloadRow render");

const [dropDownIsOpen, setDropDownIsOpen] = useState(false);

const getItemIcon = (item) => {
const extension = item.fileExst;
const icon = extension ? getIcon(32, extension) : getFolderIcon(32);

return (
<ReactSVG
beforeInjection={(svg) => {
svg.setAttribute("style", "margin-top: 4px; margin-right: 12px;");
}}
src={icon}
loading={() => <div style={{ width: "96px" }} />}
/>
);
};

const element = getItemIcon(file);

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode

import { useState, useRef, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";

import DownloadAsReactSvgUrl from "PUBLIC_DIR/images/download-as.react.svg?url";

import {
ModalDialog,
ModalDialogType,
} from "@docspace/shared/components/modal-dialog";
import { IconButton } from "@docspace/shared/components/icon-button";
import { Button } from "@docspace/shared/components/button";
import { Text } from "@docspace/shared/components/text";

import { StyledSinglePasswordFile } from "./StyledDownloadDialog";
import SimulatePassword from "../../../components/SimulatePassword";

const OnePasswordRow = ({
item,
getItemIcon,
onDownload,
downloadItems,
onClosePanel,
visible,
}) => {
const [password, setPassword] = useState("");
const { t } = useTranslation(["DownloadDialog", "Files", "Common"]);
const inputRef = useRef(null);

const onChangePassword = (password) => {
setPassword(password);
};

const updateDownloadItem = (fileId, updates) => {
const files = [...downloadItems];
const itemToUpdate = files.find((item) => item.id === fileId);
Object.assign(itemToUpdate, updates);
return files;
};

const onDowloadInOriginal = () => {
const files = updateDownloadItem(item.id, { format: item.fileExst });
onDownload(files);
};

const onDownloadWithPassword = () => {
if (!password.trim().length) return;

const files = updateDownloadItem(item.id, {
password,
});
onDownload(files);
};

const onRemoveFromDowload = () => {
const fileId = item.id;

const files = downloadItems.filter((item) => item.id !== fileId);
if (!files.length) {
onClosePanel();
return;
}
onDownload(files);
};

const onKeyUp = (event) => {
event.stopPropagation();
event.preventDefault();

if (event.key === "Enter") {
onDownloadWithPassword();
}
};

useEffect(() => {
window.addEventListener("keyup", onKeyUp, true);

return () => {
window.removeEventListener("keyup", onKeyUp, true);
};
}, [onKeyUp]);

const element = getItemIcon(item);

return (
<ModalDialog
visible={visible}
displayType={ModalDialogType.modal}
onClose={onClosePanel}
autoMaxHeight
>
<ModalDialog.Header>{t("Translations:DownloadAs")}</ModalDialog.Header>
<ModalDialog.Body>
<StyledSinglePasswordFile>
<Text>{t("AccessPasswordMessage")}</Text>
<div className="single-password_content">
<div className="single-password_row">
{element}
<Text fontWeight="600" fontSize="14px" className="password-title">
{item.title}
</Text>
</div>
<IconButton
size={16}
iconName={DownloadAsReactSvgUrl}
onClick={onDowloadInOriginal}
/>
</div>
<SimulatePassword
onChange={onChangePassword}
forwardedRef={inputRef}
/>
</StyledSinglePasswordFile>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
label={t("Common:ContinueButton")}
size="normal"
primary
onClick={onDownloadWithPassword}
isDisabled={!password.trim().length}
scale
/>
<Button
label={t("Common:CancelButton")}
size="normal"
onClick={onRemoveFromDowload}
scale
/>
</ModalDialog.Footer>
</ModalDialog>
);
};
export default inject(({ dialogsStore }) => {
const {
setDownloadItems,
downloadItems,
sortedPasswordFiles,
downloadDialogVisible: visible,
} = dialogsStore;

const item = sortedPasswordFiles[0];

return {
item,
setDownloadItems,
downloadItems,
sortedPasswordFiles,
visible,
};
})(observer(OnePasswordRow));
Loading

0 comments on commit 7a2cc02

Please sign in to comment.