diff --git a/packages/asc-web-common/api/files/index.js b/packages/asc-web-common/api/files/index.js index 3636e4e802d..cc02c22279a 100644 --- a/packages/asc-web-common/api/files/index.js +++ b/packages/asc-web-common/api/files/index.js @@ -445,8 +445,14 @@ export function setFileOwner(folderIds, fileIds, userId) { }); } -export function startUploadSession(folderId, fileName, fileSize, relativePath) { - const data = { fileName, fileSize, relativePath }; +export function startUploadSession( + folderId, + fileName, + fileSize, + relativePath, + encrypted +) { + const data = { fileName, fileSize, relativePath, encrypted }; return request({ method: "post", url: `/files/${folderId}/upload/create_session.json`, diff --git a/packages/asc-web-common/components/PageLayout/index.js b/packages/asc-web-common/components/PageLayout/index.js index 10432d7aaba..9a2c7cb2673 100644 --- a/packages/asc-web-common/components/PageLayout/index.js +++ b/packages/asc-web-common/components/PageLayout/index.js @@ -467,7 +467,7 @@ class PageLayout extends React.Component { diff --git a/products/ASC.Files/Client/public/locales/en/DeleteDialog.json b/products/ASC.Files/Client/public/locales/en/DeleteDialog.json index 45a82567f87..6db95969caf 100644 --- a/products/ASC.Files/Client/public/locales/en/DeleteDialog.json +++ b/products/ASC.Files/Client/public/locales/en/DeleteDialog.json @@ -8,5 +8,6 @@ "MoveToTrashOneFolderTitle": "Move folder to Trash?", "UnsubscribeButton": "Unsubscribe", "UnsubscribeNote": "Are you sure you want to unsubscribe from the selected items from the list?", - "UnsubscribeTitle": "Unsubscribe confirmation" -} \ No newline at end of file + "UnsubscribeTitle": "Unsubscribe confirmation", + "ConfirmRemove": "Confirmation" +} diff --git a/products/ASC.Files/Client/public/locales/en/PrivacyPage.json b/products/ASC.Files/Client/public/locales/en/PrivacyPage.json new file mode 100644 index 00000000000..b0a83dcce39 --- /dev/null +++ b/products/ASC.Files/Client/public/locales/en/PrivacyPage.json @@ -0,0 +1,10 @@ +{ + "PrivacyHeader": "This document is encrypted", + "PrivacyClick": "Click Open <1>ONLYOFFICE Desktop in the browser dialog to work with the encrypted documents", + "PrivacyDialog": "If you don’t see a dialog, click the button below", + "PrivacyButton": "Open ONLYOFFICE Desktop Editors", + "PrivacyEditors": "Don’t have ONLYOFFICE Desktop Editors", + "PrivacyInstall": "Install now", + "PrivacyDescriptionEditors": "If you have ONLYOFFICE Desktop Editors installed but can't open it from this page, your browser might be blocking it", + "PrivacyDescriptionConnect": "You can open this file from the desktop app's interface once your cloud is connected" +} diff --git a/products/ASC.Files/Client/public/locales/ru/DeleteDialog.json b/products/ASC.Files/Client/public/locales/ru/DeleteDialog.json index 0cb4c332604..80f8728df3f 100644 --- a/products/ASC.Files/Client/public/locales/ru/DeleteDialog.json +++ b/products/ASC.Files/Client/public/locales/ru/DeleteDialog.json @@ -8,5 +8,6 @@ "MoveToTrashOneFolderTitle": "Переместить папку в корзину?", "UnsubscribeButton": "Отписаться", "UnsubscribeNote": "Вы действительно хотите отписаться от выделенных элементов из списка?", - "UnsubscribeTitle": "Подтверждение отписки" + "UnsubscribeTitle": "Подтверждение отписки", + "ConfirmRemove": "Подтверждение удаления" } \ No newline at end of file diff --git a/products/ASC.Files/Client/public/locales/ru/PrivacyPage.json b/products/ASC.Files/Client/public/locales/ru/PrivacyPage.json new file mode 100644 index 00000000000..2df70983241 --- /dev/null +++ b/products/ASC.Files/Client/public/locales/ru/PrivacyPage.json @@ -0,0 +1,10 @@ +{ + "PrivacyHeader": "Этот документ зашифрован", + "PrivacyClick": "Нажмите открыть <1>ONLYOFFICE Desktop в диалоговом окне браузера, чтобы работать с зашифрованными документами", + "PrivacyDialog": "Если вы не видите диалоговое окно, нажмите кнопку ниже", + "PrivacyButton": "Откройте ONLYOFFICE Desktop Editors", + "PrivacyEditors": "Не установлены ONLYOFFICE Desktop Editors", + "PrivacyInstall": "Установить сейчас", + "PrivacyDescriptionEditors": "Если у вас установлен ONLYOFFICE Desktop Editors, но вы не можете открыть его с этой страницы, возможно, ваш браузер блокирует его", + "PrivacyDescriptionConnect": "Вы можете открыть этот файл из интерфейса настольного приложения после подключения вашего облака" +} diff --git a/products/ASC.Files/Client/src/Files.jsx b/products/ASC.Files/Client/src/Files.jsx index a3a35ba0055..12d15a60953 100644 --- a/products/ASC.Files/Client/src/Files.jsx +++ b/products/ASC.Files/Client/src/Files.jsx @@ -14,6 +14,7 @@ import { regDesktop } from "@appserver/common/desktop"; import Home from "./pages/Home"; import Settings from "./pages/Settings"; import VersionHistory from "./pages/VersionHistory"; +import PrivateRoomsPage from "./pages/PrivateRoomsPage"; import ErrorBoundary from "@appserver/common/components/ErrorBoundary"; import Panels from "./components/FilesPanels"; import { AppServerConfig } from "@appserver/common/constants"; @@ -26,6 +27,7 @@ const PROXY_HOMEPAGE_URL = combineUrl(proxyURL, homepage); const HOME_URL = combineUrl(PROXY_HOMEPAGE_URL, "/"); const SETTINGS_URL = combineUrl(PROXY_HOMEPAGE_URL, "/settings/:setting"); const HISTORY_URL = combineUrl(PROXY_HOMEPAGE_URL, "/:fileId/history"); +const PRIVATE_ROOMS_URL = combineUrl(PROXY_HOMEPAGE_URL, "/private"); const FILTER_URL = combineUrl(PROXY_HOMEPAGE_URL, "/filter"); if (!window.AppServer) { @@ -36,6 +38,7 @@ window.AppServer.files = { HOME_URL, SETTINGS_URL, HISTORY_URL, + PRIVATE_ROOMS_URL, FILTER_URL, }; @@ -86,6 +89,7 @@ class FilesContent extends React.Component { encryptionKeys, setEncryptionKeys, this.isEditor, + null, this.props.t ); console.log( @@ -106,6 +110,7 @@ class FilesContent extends React.Component { + diff --git a/products/ASC.Files/Client/src/HOCs/withBadges.js b/products/ASC.Files/Client/src/HOCs/withBadges.js index 21c07d51e84..1416daec9c3 100644 --- a/products/ASC.Files/Client/src/HOCs/withBadges.js +++ b/products/ASC.Files/Client/src/HOCs/withBadges.js @@ -131,9 +131,11 @@ export default function withBadges(WrappedComponent) { item, canWebEdit, isTrashFolder, + isPrivacyFolder, canConvert, onFilesClick, // from withFileAction HOC isAdmin, + isDesktopClient, } = this.props; const { fileStatus, access } = item; @@ -154,6 +156,8 @@ export default function withBadges(WrappedComponent) { canWebEdit={canWebEdit} canConvert={canConvert} isTrashFolder={isTrashFolder} + isPrivacyFolder={isPrivacyFolder} + isDesktopClient={isDesktopClient} accessToEdit={accessToEdit} onClickLock={this.onClickLock} onClickFavorite={this.onClickFavorite} @@ -186,13 +190,17 @@ export default function withBadges(WrappedComponent) { { item } ) => { const { docserviceStore } = formatsStore; - const { isRecycleBinFolder, updateRootBadge } = treeFoldersStore; + const { + isRecycleBinFolder, + isPrivacyFolder, + updateRootBadge, + } = treeFoldersStore; const { lockFileAction, setFavoriteAction, markAsRead, } = filesActionsStore; - const { isTabletView } = auth.settingsStore; + const { isTabletView, isDesktopClient } = auth.settingsStore; const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore; const { setNewFilesPanelVisible, @@ -214,6 +222,7 @@ export default function withBadges(WrappedComponent) { canWebEdit, canConvert, isTrashFolder: isRecycleBinFolder, + isPrivacyFolder, lockFileAction, setFavoriteAction, homepage: config.homepage, @@ -232,6 +241,7 @@ export default function withBadges(WrappedComponent) { fetchFiles, setConvertDialogVisible, setConvertItem, + isDesktopClient, }; } )(observer(WithBadges)); diff --git a/products/ASC.Files/Client/src/HOCs/withContent.js b/products/ASC.Files/Client/src/HOCs/withContent.js index 4a322f9416e..d6d5b05a9e4 100644 --- a/products/ASC.Files/Client/src/HOCs/withContent.js +++ b/products/ASC.Files/Client/src/HOCs/withContent.js @@ -202,7 +202,7 @@ export default function withContent(WrappedContent) { .then(() => { const exst = item.fileExst; return toastr.success( - + New file {{ itemTitle }}.{{ exst }} is created ); diff --git a/products/ASC.Files/Client/src/HOCs/withFileActions.js b/products/ASC.Files/Client/src/HOCs/withFileActions.js index d278ac319c3..cb86f9b5725 100644 --- a/products/ASC.Files/Client/src/HOCs/withFileActions.js +++ b/products/ASC.Files/Client/src/HOCs/withFileActions.js @@ -7,7 +7,7 @@ import Text from "@appserver/components/text"; import toastr from "@appserver/components/toast/toastr"; import { EncryptedFileIcon } from "../components/Icons"; -import { createTreeFolders } from "../helpers/files-helpers"; +import { checkProtocol, createTreeFolders } from "../helpers/files-helpers"; const svgLoader = () =>
; export default function withFileActions(WrappedFileItem) { @@ -21,7 +21,7 @@ export default function withFileActions(WrappedFileItem) { } onContentFileSelect = (checked, file) => { const { selectRowAction } = this.props; - if (!file) return; + if (!file || file.id === -1) return; selectRowAction(checked, file); }; @@ -33,7 +33,8 @@ export default function withFileActions(WrappedFileItem) { fileContextClick = () => { const { onSelectItem, item } = this.props; - onSelectItem(item); + + item.id !== -1 && onSelectItem(item); }; getSharedButton = (shared) => { @@ -63,7 +64,7 @@ export default function withFileActions(WrappedFileItem) { }; getItemIcon = (isEdit) => { - const { item, isPrivacy } = this.props; + const { item, isPrivacy, viewAs } = this.props; const { icon, fileExst } = item; return ( <> @@ -72,7 +73,9 @@ export default function withFileActions(WrappedFileItem) { src={icon} loading={svgLoader} /> - {isPrivacy && fileExst && } + {isPrivacy && fileExst && ( + + )} ); }; @@ -95,12 +98,17 @@ export default function withFileActions(WrappedFileItem) { }; onMouseDown = (e) => { - const { draggable, setTooltipPosition, setStartDrag } = this.props; + const { + draggable, + setTooltipPosition, + setStartDrag, + isPrivacy, + } = this.props; const notSelectable = e.target.classList.contains("not-selectable"); this.setState({ isMouseDown: true }); - if (!draggable) return; + if (!draggable || isPrivacy) return; if (window.innerWidth < 1025 || notSelectable) { return; @@ -149,11 +157,11 @@ export default function withFileActions(WrappedFileItem) { } else { if (checked) { this.onContentFileSelect(!checked, item); - this.fileContextClick(item); + this.fileContextClick(); } else { if (!isMouseDown) return; this.onContentFileSelect(true, item); - this.fileContextClick(item); + this.fileContextClick(); } } this.setState({ isMouseDown: false }); @@ -171,6 +179,7 @@ export default function withFileActions(WrappedFileItem) { canWebEdit, item, isTrashFolder, + isPrivacy, openDocEditor, expandedKeys, addExpandedKeys, @@ -186,7 +195,10 @@ export default function withFileActions(WrappedFileItem) { providerKey, contentLength, fileStatus, + encrypted, } = item; + if (encrypted && isPrivacy) return checkProtocol(item.id, true); + if (isTrashFolder) return; if (e && e.target.tagName === "INPUT") return; @@ -234,7 +246,7 @@ export default function withFileActions(WrappedFileItem) { render() { const { item, - isRecycleBin, + isTrashFolder, draggable, canShare, isPrivacy, @@ -245,13 +257,14 @@ export default function withFileActions(WrappedFileItem) { checked, dragging, isFolder, + isDesktop, } = this.props; const { fileExst, access, contentLength, id, shared } = item; const isEdit = - !!actionType && actionId === id && fileExst === actionExtension; + actionType !== null && actionId === id && fileExst === actionExtension; - const isDragging = isFolder && access < 2 && !isRecycleBin; + const isDragging = isFolder && access < 2 && !isTrashFolder && !isPrivacy; let className = isDragging ? " droppable" : ""; if (draggable) className += " draggable not-selectable"; @@ -266,8 +279,10 @@ export default function withFileActions(WrappedFileItem) { ? "38px" : "96px"; + const showShare = isPrivacy && (!isDesktop || !fileExst) ? false : true; + const sharedButton = - !canShare || (isPrivacy && !fileExst) || isEdit || id <= 0 || isMobile + !canShare || !showShare || isEdit || id <= 0 || isMobile ? null : this.getSharedButton(shared); @@ -293,6 +308,7 @@ export default function withFileActions(WrappedFileItem) { checkedProps={checkedProps} element={element} dragging={dragging} + isEdit={isEdit} {...this.props} /> ); @@ -302,6 +318,7 @@ export default function withFileActions(WrappedFileItem) { return inject( ( { + auth, filesActionsStore, dialogsStore, treeFoldersStore, @@ -412,6 +429,7 @@ export default function withFileActions(WrappedFileItem) { viewAs, setConvertItem, setConvertDialogVisible, + isDesktop: auth.settingsStore.isDesktopClient, }; } )(observer(WithFileActions)); diff --git a/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js b/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js index 42560839af9..491ee24d548 100644 --- a/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js +++ b/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js @@ -213,7 +213,6 @@ class TreeFolders extends React.Component { key={item.id} className={className} title={item.title} - needTopMargin={item.rootFolderType === FolderType.Privacy} icon={this.getFolderIcon(item)} dragging={dragging} isLeaf={ @@ -246,7 +245,13 @@ class TreeFolders extends React.Component { title={item.title} needTopMargin={item.rootFolderType === FolderType.TRASH} dragging={dragging} - isLeaf={item.foldersCount ? false : true} + isLeaf={ + (item.rootFolderType === FolderType.Privacy && + !this.props.isDesktop) || + !item.foldersCount + ? true + : false + } icon={this.getFolderIcon(item)} newItems={ !this.props.isDesktop && item.rootFolderType === FolderType.Privacy diff --git a/products/ASC.Files/Client/src/components/Badges.js b/products/ASC.Files/Client/src/components/Badges.js index 0523f0be168..615e6f7f79a 100644 --- a/products/ASC.Files/Client/src/components/Badges.js +++ b/products/ASC.Files/Client/src/components/Badges.js @@ -13,6 +13,8 @@ const Badges = ({ item, canWebEdit, isTrashFolder, + isPrivacyFolder, + isDesktopClient, canConvert, accessToEdit, showNew, @@ -29,6 +31,7 @@ const Badges = ({ const isEditing = fileStatus === 1; const isNewWithFav = fileStatus === 34; const showEditBadge = !locked || item.access === 0; + const isPrivacy = isPrivacyFolder && isDesktopClient; return fileExst ? (
@@ -46,6 +49,7 @@ const Badges = ({ {canWebEdit && !isEditing && !isTrashFolder && + isPrivacy && accessToEdit && showEditBadge && !canConvert && ( diff --git a/products/ASC.Files/Client/src/components/EditingWrapperComponent.js b/products/ASC.Files/Client/src/components/EditingWrapperComponent.js index 1c73faf7720..829564a8458 100644 --- a/products/ASC.Files/Client/src/components/EditingWrapperComponent.js +++ b/products/ASC.Files/Client/src/components/EditingWrapperComponent.js @@ -131,7 +131,7 @@ const EditingWrapperComponent = (props) => { data-itemid={itemId} />
@@ -552,6 +560,7 @@ class SharingPanelComponent extends React.Component { groupsCaption={groupsCaption} accessOptions={accessOptions} isMultiSelect + isEncrypted={isEncrypted} /> )} diff --git a/products/ASC.Files/Client/src/helpers/constants.js b/products/ASC.Files/Client/src/helpers/constants.js index 25bc045fe4d..da4504f58d7 100644 --- a/products/ASC.Files/Client/src/helpers/constants.js +++ b/products/ASC.Files/Client/src/helpers/constants.js @@ -1,5 +1,6 @@ export const GUID_EMPTY = "00000000-0000-0000-0000-000000000000"; export const TIMEOUT = 1000; +export const EDITOR_PROTOCOL = "oo-office"; export const thumbnailStatuses = { WAITING: 0, diff --git a/products/ASC.Files/Client/src/helpers/files-helpers.js b/products/ASC.Files/Client/src/helpers/files-helpers.js index ce53a7f7874..d82fe7b77c0 100644 --- a/products/ASC.Files/Client/src/helpers/files-helpers.js +++ b/products/ASC.Files/Client/src/helpers/files-helpers.js @@ -1,4 +1,5 @@ import { runInAction } from "mobx"; +import { EDITOR_PROTOCOL } from "./constants"; export const presentInArray = (array, search, caseInsensitive = false) => { let pattern = caseInsensitive ? search.toLowerCase() : search; @@ -142,3 +143,26 @@ export const loopTreeFolders = ( } }); }; + +export const checkProtocol = (fileId, withRedirect) => + new Promise((resolve, reject) => { + const onBlur = () => { + clearTimeout(timeout); + window.removeEventListener("blur", onBlur); + resolve(); + }; + + const timeout = setTimeout(() => { + reject(); + window.removeEventListener("blur", onBlur); + withRedirect && + window.open(`/products/files/private?fileId=${fileId}`, "_blank"); + }, 1000); + + window.addEventListener("blur", onBlur); + + window.open( + `${EDITOR_PROTOCOL}:${window.location.origin}/products/files/doceditor?fileId=${fileId}`, + "_self" + ); + }); diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/SimpleFilesRow.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/SimpleFilesRow.js index 573b0ec845a..1069af79fb2 100644 --- a/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/SimpleFilesRow.js +++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/SimpleFilesRow.js @@ -41,6 +41,8 @@ const StyledSimpleFilesRow = styled(Row)` } .styled-element { + height: 32px; + width: ${(props) => (props.isEdit ? "52px" : "24px")}; margin-right: 7px; } `; @@ -65,6 +67,7 @@ const SimpleFilesRow = (props) => { element, onFilesClick, onMouseUp, + isEdit, } = props; return ( @@ -81,6 +84,7 @@ const SimpleFilesRow = (props) => { { }; const onDropEvent = () => { - dragging && setDragging(false); + setDragging(false); }; const onDragOver = (e) => { diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js index 57d64315229..c8c5450a6f6 100644 --- a/products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js +++ b/products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js @@ -8,7 +8,7 @@ import Loaders from "@appserver/common/components/Loaders"; import FilterInput from "@appserver/common/components/FilterInput"; import { withLayoutSize } from "@appserver/common/utils"; //import equal from "fast-deep-equal/react"; -import { isMobileOnly } from "react-device-detect"; +import { isMobileOnly, isMobile } from "react-device-detect"; import { inject, observer } from "mobx-react"; const getFilterType = (filterValues) => { @@ -307,39 +307,47 @@ class SectionFilterContent extends React.Component { } } -export default inject(({ auth, filesStore, selectedFolderStore }) => { - const { - fetchFiles, - filter, - setIsLoading, - setViewAs, - viewAs, - files, - folders, - createThumbnails, - } = filesStore; - - const { user } = auth.userStore; - const { customNames, culture } = auth.settingsStore; - - const { search, filterType, authorType } = filter; - const isFiltered = - !!files.length || !!folders.length || search || filterType || authorType; - - return { - customNames, - user, - selectedFolderId: selectedFolderStore.id, - selectedItem: filter.selectedItem, - filter, - viewAs, - isFiltered, - setIsLoading, - fetchFiles, - setViewAs, - createThumbnails, - }; -})( +export default inject( + ({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => { + const { + fetchFiles, + filter, + setIsLoading, + setViewAs, + viewAs, + files, + folders, + createThumbnails, + } = filesStore; + + const { user } = auth.userStore; + const { customNames, culture } = auth.settingsStore; + + const { search, filterType, authorType } = filter; + const isFiltered = + (!!files.length || + !!folders.length || + search || + filterType || + authorType) && + !(treeFoldersStore.isPrivacyFolder && isMobile); + + return { + customNames, + user, + selectedFolderId: selectedFolderStore.id, + selectedItem: filter.selectedItem, + filter, + viewAs, + isFiltered, + + setIsLoading, + fetchFiles, + setViewAs, + createThumbnails, + }; + } +)( withRouter( withLayoutSize( withTranslation(["Home", "Common", "Translations"])( diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Header/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Header/index.js index 469bd013ecf..8e7cdcd1127 100644 --- a/products/ASC.Files/Client/src/pages/Home/Section/Header/index.js +++ b/products/ASC.Files/Client/src/pages/Home/Section/Header/index.js @@ -384,7 +384,8 @@ class SectionHeaderContent extends React.Component { isFavoritesFolder || isRecentFolder || !isAccessedSelected || - (isPrivacy && (isOnlyFoldersSelected || selectionCount > 1)), + isOnlyFoldersSelected || + selectionCount > 1, onClick: this.onOpenSharingPanel, }, { @@ -435,8 +436,9 @@ class SectionHeaderContent extends React.Component { } if (isPrivacy) { + menu.splice(1, 1); + menu.splice(2, 1); menu.splice(3, 1); - menu.splice(4, 1); } if (isShareFolder) { diff --git a/products/ASC.Files/Client/src/pages/Home/index.js b/products/ASC.Files/Client/src/pages/Home/index.js index 28e829acf2d..80a422b8d21 100644 --- a/products/ASC.Files/Client/src/pages/Home/index.js +++ b/products/ASC.Files/Client/src/pages/Home/index.js @@ -225,6 +225,7 @@ class PureHome extends React.Component { fileActionId, firstLoad, isHeaderVisible, + isPrivacyFolder, isRecycleBinFolder, primaryProgressDataVisible, @@ -249,7 +250,7 @@ class PureHome extends React.Component { withBodyScroll withBodyAutoFocus={!isMobile} uploadFiles - onDrop={isRecycleBinFolder ? null : this.onDrop} + onDrop={isRecycleBinFolder || isPrivacyFolder ? null : this.onDrop} setSelections={this.props.setSelections} onMouseMove={this.onMouseMove} showPrimaryProgressBar={primaryProgressDataVisible} @@ -329,6 +330,7 @@ export default inject( const { id } = fileActionStore; const { isRecycleBinFolder, + isPrivacyFolder, expandedKeys, setExpandedKeys, } = treeFoldersStore; @@ -371,6 +373,7 @@ export default inject( uploaded, converted, isRecycleBinFolder, + isPrivacyFolder, isVisitor: auth.userStore.user.isVisitor, expandedKeys, diff --git a/products/ASC.Files/Client/src/pages/PrivateRoomsPage/i18n.js b/products/ASC.Files/Client/src/pages/PrivateRoomsPage/i18n.js new file mode 100644 index 00000000000..8cf51d7dbe4 --- /dev/null +++ b/products/ASC.Files/Client/src/pages/PrivateRoomsPage/i18n.js @@ -0,0 +1,32 @@ +import i18n from "i18next"; +import Backend from "i18next-http-backend"; +import { LANGUAGE } from "@appserver/common/constants"; +import config from "../../../package.json"; +import { loadLanguagePath } from "@appserver/common/utils"; + +const newInstance = i18n.createInstance(); + +newInstance.use(Backend).init({ + lng: localStorage.getItem(LANGUAGE) || "en", + fallbackLng: "en", + load: "all", + //debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + format: function (value, format) { + if (format === "lowercase") return value.toLowerCase(); + return value; + }, + }, + + backend: { + loadPath: loadLanguagePath(config.homepage, "PrivacyPage"), + }, + + react: { + useSuspense: false, + }, +}); + +export default newInstance; diff --git a/products/ASC.Files/Client/src/pages/PrivateRoomsPage/index.js b/products/ASC.Files/Client/src/pages/PrivateRoomsPage/index.js new file mode 100644 index 00000000000..35bdf42fb9d --- /dev/null +++ b/products/ASC.Files/Client/src/pages/PrivateRoomsPage/index.js @@ -0,0 +1,214 @@ +import React, { useEffect, useState } from "react"; +import styled from "styled-components"; +import Text from "@appserver/components/text"; +import Link from "@appserver/components/link"; +import Button from "@appserver/components/button"; +import Loader from "@appserver/components/loader"; +import PageLayout from "@appserver/common/components/PageLayout"; +import { smallTablet, tablet } from "@appserver/components/utils/device"; +import { I18nextProvider, Trans, withTranslation } from "react-i18next"; +import { withRouter } from "react-router"; +import { isMobile } from "react-device-detect"; +//import { setDocumentTitle } from "../../helpers/utils"; +import i18n from "./i18n"; +import toastr from "studio/toastr"; +import { checkProtocol } from "../../helpers/files-helpers"; + +const StyledPrivacyPage = styled.div` + margin-top: ${isMobile ? "80px" : "36px"}; + + .privacy-rooms-body { + display: flex; + flex-direction: column; + align-items: center; + max-width: 770px; + margin: auto; + margin-top: 80px; + } + + .privacy-rooms-text-header { + margin-bottom: 46px; + } + + .privacy-rooms-text-dialog { + margin-top: 32px; + margin-bottom: 42px; + } + + .privacy-rooms-text-separator { + width: 70%; + margin: 28px 0 42px 0; + border-bottom: 1px solid #d3d3d3; + } + + .privacy-rooms-install-text { + text-align: left; + + @media ${smallTablet} { + text-align: center; + } + } + + .privacy-rooms-install { + display: flex; + flex-direction: row; + + @media ${smallTablet} { + flex-direction: column; + } + } + + .privacy-rooms-link { + margin-left: 4px; + } + + .privacy-rooms-text-description { + margin-top: 28px; + + p { + margin: 0; + } + } + + .privacy-rooms-avatar { + text-align: left; + padding-left: 66px; + + @media ${tablet} { + padding-left: 74px; + } + + @media ${smallTablet} { + padding: 0px; + text-align: center; + } + + margin: 0px; + } + + .privacy-rooms-logo { + text-align: center; + max-width: 216px; + max-height: 35px; + } +`; + +const PrivacyPageComponent = ({ t, history, tReady }) => { + // useEffect(() => { + // setDocumentTitle(t("Common:About")); + // }, [t]); + + const [isDisabled, setIsDisabled] = useState(false); + + const onOpenEditorsPopup = async () => { + setIsDisabled(true); + checkProtocol(history.location.search.split("=")[1]) + .then(() => setIsDisabled(false)) + .catch(() => { + setIsDisabled(false); + toastr.info(t("PrivacyEditors")); + }); + }; + + return !tReady ? ( + + ) : ( + +
+ + Logo + +
+ +
+ + {t("PrivacyHeader")} + + + + + Click Open ONLYOFFICE Desktop in the browser dialog + to work with the encrypted documents + + . + + + + {t("PrivacyDialog")}. + +
+
+ ); +}; + +const PrivacyPageWrapper = withTranslation(["PrivacyPage"])( + withRouter(PrivacyPageComponent) +); + +const PrivacyPage = (props) => { + return ( + + + + + + + + ); +}; + +export default PrivacyPage; diff --git a/products/ASC.Files/Client/src/store/FilesActionsStore.js b/products/ASC.Files/Client/src/store/FilesActionsStore.js index 84c3b5af76f..458aff8d6d8 100644 --- a/products/ASC.Files/Client/src/store/FilesActionsStore.js +++ b/products/ASC.Files/Client/src/store/FilesActionsStore.js @@ -313,7 +313,10 @@ class FilesActionStore { const { setSecondaryProgressBarData, } = this.uploadDataStore.secondaryProgressDataStore; - if (this.settingsStore.confirmDelete) { + if ( + this.settingsStore.confirmDelete || + this.treeFoldersStore.isPrivacyFolder + ) { this.dialogsStore.setDeleteDialogVisible(true); } else { setSecondaryProgressBarData({ diff --git a/products/ASC.Files/Client/src/store/FilesStore.js b/products/ASC.Files/Client/src/store/FilesStore.js index b237147b49c..fbe6efde554 100644 --- a/products/ASC.Files/Client/src/store/FilesStore.js +++ b/products/ASC.Files/Client/src/store/FilesStore.js @@ -14,6 +14,7 @@ import { combineUrl } from "@appserver/common/utils"; import { updateTempContent } from "@appserver/common/utils"; import { loopTreeFolders } from "../helpers/files-helpers"; import { thumbnailStatuses } from "../helpers/constants"; +import { isMobile } from "react-device-detect"; const { FilesFilter } = api; @@ -266,26 +267,26 @@ class FilesStore { } = this.treeFoldersStore; setSelectedNode([folderId + ""]); - if (privacyFolder && privacyFolder.id === +folderId) { - if (!this.settingsStore.isEncryptionSupport) { - filterData.total = 0; - this.setFilesFilter(filterData); //TODO: FILTER - if (clearFilter) { - this.setFolders([]); - this.setFiles([]); - this.fileActionStore.setAction({ type: null }); - this.setSelected("close"); - - this.selectedFolderStore.setSelectedFolder({ - folders: [], - ...privacyFolder, - pathParts: privacyFolder.pathParts, - ...{ new: 0 }, - }); - } - return Promise.resolve(); - } - } + // if (privacyFolder && privacyFolder.id === +folderId) { + // if (!this.settingsStore.isEncryptionSupport) { + // filterData.total = 0; + // this.setFilesFilter(filterData); //TODO: FILTER + // if (clearFilter) { + // this.setFolders([]); + // this.setFiles([]); + // this.fileActionStore.setAction({ type: null }); + // this.setSelected("close"); + + // this.selectedFolderStore.setSelectedFolder({ + // folders: [], + // ...privacyFolder, + // pathParts: privacyFolder.pathParts, + // ...{ new: 0 }, + // }); + // } + // return Promise.resolve(); + // } + // } //TODO: fix @my let requestCounter = 1; @@ -308,16 +309,9 @@ class FilesStore { filterData.total = data.total; this.setFilesFilter(filterData); //TODO: FILTER - this.setFolders( - isPrivacyFolder && !this.settingsStore.isEncryptionSupport - ? [] - : data.folders - ); - this.setFiles( - isPrivacyFolder && !this.settingsStore.isEncryptionSupport - ? [] - : data.files - ); + this.setFolders(isPrivacyFolder && isMobile ? [] : data.folders); + this.setFiles(isPrivacyFolder && isMobile ? [] : data.files); + if (clearFilter) { this.fileActionStore.setAction({ type: null }); this.setSelected("close"); @@ -393,6 +387,7 @@ class FilesStore { const isEncrypted = item.encrypted; const isDocuSign = false; //TODO: need this prop; const isEditing = false; //TODO: need this prop; + const isFileOwner = item.createdBy.id === this.userStore.user.id; const { isRecycleBinFolder, @@ -526,7 +521,7 @@ class FilesStore { } } - if (isFavoritesFolder || isPrivacyFolder || isRecentFolder) { + if (isFavoritesFolder || isRecentFolder) { fileOptions = this.removeOptions(fileOptions, [ "copy", "move-to", @@ -641,10 +636,29 @@ class FilesStore { "delete", ]); } - } else { + } else if (!isEncrypted) { fileOptions = this.removeOptions(fileOptions, ["unsubscribe"]); } + if (isPrivacyFolder) { + fileOptions = this.removeOptions(fileOptions, [ + "preview", + "view", + "separator0", + "copy", + "download-as", + ]); + + if (!this.authStore.settingsStore.isDesktopClient) { + fileOptions = this.removeOptions(fileOptions, ["sharing-settings"]); + } + + fileOptions = this.removeOptions( + fileOptions, + isFileOwner ? ["unsubscribe"] : ["move-to", "delete"] + ); + } + return fileOptions; } else { let folderOptions = [ @@ -668,7 +682,15 @@ class FilesStore { ]; if (isPrivacyFolder) { - folderOptions = this.removeOptions(folderOptions, ["copy"]); + folderOptions = this.removeOptions(folderOptions, [ + "sharing-settings", + "copy", + "copy-to", + ]); + + if (!this.authStore.settingsStore.isDesktopClient) { + folderOptions = this.removeOptions(folderOptions, ["rename"]); + } } if (isShareItem) { @@ -989,6 +1011,7 @@ class FilesStore { contentLength, created, createdBy, + encrypted, fileExst, filesCount, fileStatus, @@ -1038,6 +1061,7 @@ class FilesStore { contextOptions, created, createdBy, + encrypted, fileExst, filesCount, fileStatus, @@ -1172,6 +1196,10 @@ class FilesStore { canWebFilterEditing, } = this.formatsStore.docserviceStore; + if (selection[0].encrypted) { + return ["FullAccess", "DenyAccess"]; + } + let AccessOptions = []; AccessOptions.push("ReadOnly", "DenyAccess"); @@ -1229,6 +1257,7 @@ class FilesStore { } } + //this.selected === "close" && this.setSelected("none"); this.setSelection(newSelection); //} }; diff --git a/products/ASC.Files/Client/src/store/UploadDataStore.js b/products/ASC.Files/Client/src/store/UploadDataStore.js index c8ff52e200f..af635ede753 100644 --- a/products/ASC.Files/Client/src/store/UploadDataStore.js +++ b/products/ASC.Files/Client/src/store/UploadDataStore.js @@ -418,6 +418,7 @@ class UploadDataStore { fileInfo: null, cancel: false, needConvert, + encrypted: file.encrypted, }; needConvert @@ -635,7 +636,13 @@ class UploadDataStore { ? file.webkitRelativePath.slice(0, -file.name.length) : ""; - return startUploadSession(toFolderId, fileName, fileSize, relativePath) + return startUploadSession( + toFolderId, + fileName, + fileSize, + relativePath, + file.encrypted + ) .then((res) => { const location = res.data.location; diff --git a/products/ASC.People/Client/src/components/Article/MainButton/index.js b/products/ASC.People/Client/src/components/Article/MainButton/index.js index f1b63886beb..37af113c81b 100644 --- a/products/ASC.People/Client/src/components/Article/MainButton/index.js +++ b/products/ASC.People/Client/src/components/Article/MainButton/index.js @@ -59,6 +59,7 @@ class PureArticleMainButtonContent extends React.Component { //console.log("People ArticleMainButtonContent render"); const { t, + tReady, isLoaded, isAdmin, homepage, @@ -71,7 +72,7 @@ class PureArticleMainButtonContent extends React.Component { return ( isAdmin && - (!isLoaded ? ( + (!isLoaded || !tReady ? ( ) : ( <> diff --git a/products/ASC.People/Client/src/components/PeopleSelector/index.js b/products/ASC.People/Client/src/components/PeopleSelector/index.js index 86dd6fdbd66..5f931a8156a 100644 --- a/products/ASC.People/Client/src/components/PeopleSelector/index.js +++ b/products/ASC.People/Client/src/components/PeopleSelector/index.js @@ -231,6 +231,8 @@ class PeopleSelector extends React.Component { showCounter, } = this.props; + console.log("CustomAllGroups", t("CustomAllGroups", { groupsCaption })); + console.log("PeopleSelector render"); return ( { group, isAdmin, t, + tReady, history, customNames, homepage, @@ -462,7 +463,7 @@ const SectionHeaderContent = (props) => { ) : (
- {!isLoaded ? ( + {!isLoaded || !tReady ? ( ) : group ? ( <> diff --git a/products/ASC.People/Client/src/pages/Home/Section/Paging/index.js b/products/ASC.People/Client/src/pages/Home/Section/Paging/index.js index bb0475fd7e6..a1b778a3c3f 100644 --- a/products/ASC.People/Client/src/pages/Home/Section/Paging/index.js +++ b/products/ASC.People/Client/src/pages/Home/Section/Paging/index.js @@ -11,6 +11,7 @@ const SectionPagingContent = ({ setIsLoading, selectedCount, isLoaded, + tReady, }) => { const { t } = useTranslation("Home"); const onNextClick = useCallback( @@ -125,7 +126,7 @@ const SectionPagingContent = ({ //console.log("SectionPagingContent render", filter); return isLoaded ? ( - !filter || filter.total < filter.pageCount ? ( + !filter || filter.total < filter.pageCount || !tReady ? ( <> ) : ( @@ -172,10 +173,10 @@ class NavMenu extends React.Component { withBackground={true} /> - {!isDesktop && + {!hideHeader && (isLoaded && isAuthenticated ? ( <> - +
{ isDesktop, language, }; -})(observer(withTranslation(["NavMenu", "Common"])(NavMenu))); +})(observer(withTranslation(["NavMenu", "Common"])(withRouter(NavMenu)))); export default () => (