diff --git a/build/install/common/publish-backend.sh b/build/install/common/publish-backend.sh
index 3a0c3b87c4..93a54547d1 100644
--- a/build/install/common/publish-backend.sh
+++ b/build/install/common/publish-backend.sh
@@ -60,6 +60,8 @@ servers_products_name_backend=(ASC.CRM)
servers_products_name_backend+=(ASC.Files)
servers_products_name_backend+=(ASC.People)
servers_products_name_backend+=(ASC.Projects)
+servers_products_name_backend+=(ASC.Calendar)
+servers_products_name_backend+=(ASC.Mail)
# Publish server backend products
for i in ${!servers_products_name_backend[@]}; do
diff --git a/build/install/docker/.env b/build/install/docker/.env
index 823635a2ee..ddf27d85e8 100644
--- a/build/install/docker/.env
+++ b/build/install/docker/.env
@@ -47,10 +47,12 @@
# service host #
API_SYSTEM_HOST=${CONTAINER_PREFIX}api-system
BACKUP_HOST=${CONTAINER_PREFIX}backup
+ CALENDAR_HOST=${CONTAINER_PREFIX}calendar
CRM_HOST=${CONTAINER_PREFIX}crm
STORAGE_ENCRYPTION_HOST=${CONTAINER_PREFIX}storage-encryption
FILES_HOST=${CONTAINER_PREFIX}files
FILES_SERVICES_HOST=${CONTAINER_PREFIX}files-services
+ MAIL_HOST=${CONTAINER_PREFIX}mail
STORAGE_MIGRATION_HOST=${CONTAINER_PREFIX}storage-migration
NOTIFY_HOST=${CONTAINER_PREFIX}notify
PEOPLE_SERVER_HOST=${CONTAINER_PREFIX}people-server
@@ -68,9 +70,11 @@
SERVICE_API_SYSTEM=${API_SYSTEM_HOST}:${SERVICE_PORT}
SERVICE_BACKUP=${BACKUP_HOST}:${SERVICE_PORT}
SERVICE_CRM=${CRM_HOST}:${SERVICE_PORT}
+ SERVICE_CALENDAR=${CALENDAR_HOST}:${SERVICE_PORT}
SERVICE_STORAGE_ENCRYPTION=${STORAGE_ENCRYPTION_HOST}:${SERVICE_PORT}
SERVICE_FILES=${FILES_HOST}:${SERVICE_PORT}
SERVICE_FILES_SERVICES=${FILES_SERVICES_HOST}:${SERVICE_PORT}
+ SERVICE_MAIL=${MAIL_HOST}:${SERVICE_PORT}
SERVICE_STORAGE_MIGRATION=${STORAGE_MIGRATION_HOST}:${SERVICE_PORT}
SERVICE_NOTIFY=${NOTIFY_HOST}:${SERVICE_PORT}
SERVICE_PEOPLE_SERVER=${PEOPLE_SERVER_HOST}:${SERVICE_PORT}
diff --git a/build/install/docker/Dockerfile-app b/build/install/docker/Dockerfile-app
index dd3d677de8..2ab5d1bdd3 100644
--- a/build/install/docker/Dockerfile-app
+++ b/build/install/docker/Dockerfile-app
@@ -45,11 +45,8 @@ RUN echo "nameserver 8.8.8.8" | tee /etc/resolv.conf > /dev/null && \
bash build-frontend.sh -sp ${SRC_PATH} && \
bash build-backend.sh -sp ${SRC_PATH} -ar "--disable-parallel" && \
bash publish-backend.sh -sp ${SRC_PATH} -bp ${BUILD_PATH} -ar "--disable-parallel"
-
-COPY config/mysql/conf.d/mysql.cnf /etc/mysql/conf.d/mysql.cnf
-COPY config/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
-RUN sed -i 's/Server=.*;Port=/Server=127.0.0.1;Port=/' /app/onlyoffice/config/appsettings.test.json
+COPY config/mysql/conf.d/mysql.cnf /etc/mysql/conf.d/mysql.cnf
RUN rm -rf /var/lib/apt/lists/*
@@ -107,6 +104,8 @@ COPY --from=base ${SRC_PATH}/web/ASC.Web.Login/dist ${BUILD_PATH}/studio/login
COPY --from=base ${SRC_PATH}/products/ASC.People/Client/dist ${BUILD_PATH}/products/ASC.People/client
COPY --from=base ${SRC_PATH}/products/ASC.Projects/Client/dist ${BUILD_PATH}/products/ASC.Projects/client
COPY --from=base ${SRC_PATH}/web/ASC.Web.Client/dist ${BUILD_PATH}/studio/client
+COPY --from=base ${SRC_PATH}/products/ASC.Calendar/Client/dist ${BUILD_PATH}/products/ASC.Calendar/client
+COPY --from=base ${SRC_PATH}/products/ASC.Mail/Client/dist ${BUILD_PATH}/products/ASC.Mail/client
COPY /config/nginx/templates/upstream.conf.template /etc/nginx/templates/upstream.conf.template
@@ -122,6 +121,8 @@ RUN chown nginx:nginx /etc/nginx/* -R && \
sed -i 's/localhost:5020/$service_projects_server/' /etc/nginx/conf.d/onlyoffice.conf && \
sed -i 's/localhost:5000/$service_api/' /etc/nginx/conf.d/onlyoffice.conf && \
sed -i 's/localhost:5003/$service_studio/' /etc/nginx/conf.d/onlyoffice.conf && \
+ sed -i 's/localhost:5023/$service_calendar/' /etc/nginx/conf.d/onlyoffice.conf && \
+ sed -i 's/localhost:5022/$service_mail/' /etc/nginx/conf.d/onlyoffice.conf && \
sed -i 's/localhost:9999/$service_urlshortener/' /etc/nginx/conf.d/onlyoffice.conf && \
sed -i 's/172.*/$document_server;/' /etc/nginx/conf.d/onlyoffice.conf && \
# configute the image nginx whith less privileged https://hub.docker.com/_/nginx
@@ -146,6 +147,15 @@ COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Data.B
CMD ["ASC.Data.Backup.dll", "ASC.Data.Backup", "core:products:folder=/var/www/products/", "core:products:subfolder=server"]
+## ASC.Calendar ##
+FROM builder AS calendar
+WORKDIR ${BUILD_PATH}/products/ASC.Calendar/server/
+
+COPY --chown=onlyoffice:onlyoffice docker-entrypoint.sh .
+COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/products/ASC.Calendar/server/ .
+
+CMD ["ASC.Calendar.dll", "ASC.Calendar"]
+
## ASC.CRM ##
FROM builder AS crm
WORKDIR ${BUILD_PATH}/products/ASC.CRM/server/
@@ -182,6 +192,15 @@ COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Files.
CMD ["ASC.Files.Service.dll", "ASC.Files.Service", "core:products:folder=/var/www/products/", "core:products:subfolder=server"]
+## ASC.Mail ##
+FROM builder AS mail
+WORKDIR ${BUILD_PATH}/products/ASC.Mail/server/
+
+COPY --chown=onlyoffice:onlyoffice docker-entrypoint.sh .
+COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/products/ASC.Mail/server/ .
+
+CMD ["ASC.Mail.dll", "ASC.Mail"]
+
## ASC.Data.Storage.Migration ##
FROM builder AS data_storage_migration
WORKDIR ${BUILD_PATH}/services/storage.migration/service/
diff --git a/build/install/docker/appserver.yml b/build/install/docker/appserver.yml
index 3eef1db072..4912140010 100644
--- a/build/install/docker/appserver.yml
+++ b/build/install/docker/appserver.yml
@@ -29,6 +29,9 @@ x-service:
- people_data:/var/www/products/ASC.People/server/
- crm_data:/var/www/products/ASC.CRM/server/
- project_data:/var/www/products/ASC.Projects/server/
+ - calendar_data:/var/www/products/ASC.Calendar/server/
+ - mail_data:/var/www/products/ASC.Mail/server/
+
services:
onlyoffice-elasticsearch:
@@ -95,6 +98,11 @@ services:
image: "${REPO}/${STATUS}appserver-backup:${SRV_VERSION}"
container_name: ${BACKUP_HOST}
+ onlyoffice-calendar:
+ <<: *x-service-base
+ image: "${REPO}/${STATUS}appserver-calendar:${SRV_VERSION}"
+ container_name: ${CALENDAR_HOST}
+
onlyoffice-crm:
<<: *x-service-base
image: "${REPO}/${STATUS}appserver-crm:${SRV_VERSION}"
@@ -115,6 +123,11 @@ services:
image: "${REPO}/${STATUS}appserver-files-services:${SRV_VERSION}"
container_name: ${FILES_SERVICES_HOST}
+ onlyoffice-mail:
+ <<: *x-service-base
+ image: "${REPO}/${STATUS}appserver-mail:${SRV_VERSION}"
+ container_name: ${MAIL_HOST}
+
onlyoffice-storage-migration:
<<: *x-service-base
image: "${REPO}/${STATUS}appserver-storage-migration:${SRV_VERSION}"
@@ -187,10 +200,12 @@ services:
depends_on:
- onlyoffice-api-system
- onlyoffice-backup
+ - onlyoffice-calendar
- onlyoffice-crm
- onlyoffice-storage-encryption
- onlyoffice-files
- onlyoffice-files-services
+ - onlyoffice-mail
- onlyoffice-storage-migration
- onlyoffice-people-server
- onlyoffice-projects-server
@@ -204,10 +219,12 @@ services:
environment:
- SERVICE_API_SYSTEM=${SERVICE_API_SYSTEM}
- SERVICE_BACKUP=${SERVICE_BACKUP}
+ - SERVICE_CALENDAR=${SERVICE_CALENDAR}
- SERVICE_CRM=${SERVICE_CRM}
- SERVICE_STORAGE_ENCRYPTION=${SERVICE_STORAGE_ENCRYPTION}
- SERVICE_FILES=${SERVICE_FILES}
- SERVICE_FILES_SERVICES=${SERVICE_FILES_SERVICES}
+ - SERVICE_MAIL=${SERVICE_MAIL}
- SERVICE_STORAGE_MIGRATION=${SERVICE_STORAGE_MIGRATION}
- SERVICE_NOTIFY=${SERVICE_NOTIFY}
- SERVICE_PEOPLE_SERVER=${SERVICE_PEOPLE_SERVER}
@@ -240,3 +257,5 @@ volumes:
people_data:
crm_data:
project_data:
+ calendar_data:
+ mail_data:
diff --git a/build/install/docker/build.yml b/build/install/docker/build.yml
index 1e6209d635..9a43b41e51 100644
--- a/build/install/docker/build.yml
+++ b/build/install/docker/build.yml
@@ -15,6 +15,13 @@ services:
target: backup
image: "${REPO}/${STATUS}appserver-backup:${SRV_VERSION}"
+ onlyoffice-calendar:
+ build:
+ context: ./
+ dockerfile: "${DOCKERFILE}"
+ target: calendar
+ image: "${REPO}/${STATUS}appserver-calendar:${SRV_VERSION}"
+
onlyoffice-crm:
build:
context: ./
@@ -43,6 +50,13 @@ services:
target: files_services
image: "${REPO}/${STATUS}appserver-files-services:${SRV_VERSION}"
+ onlyoffice-mail:
+ build:
+ context: ./
+ dockerfile: "${DOCKERFILE}"
+ target: mail
+ image: "${REPO}/${STATUS}appserver-mail:${SRV_VERSION}"
+
onlyoffice-storage-migration:
build:
context: ./
diff --git a/build/install/docker/config/nginx/templates/upstream.conf.template b/build/install/docker/config/nginx/templates/upstream.conf.template
index 675432e66c..a14517cd5a 100644
--- a/build/install/docker/config/nginx/templates/upstream.conf.template
+++ b/build/install/docker/config/nginx/templates/upstream.conf.template
@@ -10,6 +10,11 @@ map $SERVICE_BACKUP $service_backup {
$SERVICE_BACKUP $SERVICE_BACKUP;
}
+map $SERVICE_CALENDAR $service_calendar {
+ volatile;
+ $SERVICE_CALENDAR $SERVICE_CALENDAR;
+}
+
map $SERVICE_CRM $service_crm {
volatile;
$SERVICE_CRM $SERVICE_CRM;
@@ -30,6 +35,11 @@ map $SERVICE_FILES_SERVICES $service_files_services {
$SERVICE_FILES_SERVICES $SERVICE_FILES_SERVICES;
}
+map $SERVICE_MAIL $service_mail {
+ volatile;
+ $SERVICE_MAIL $SERVICE_MAIL;
+}
+
map $SERVICE_STORAGE_MIGRATION $service_storage_migration {
volatile;
$SERVICE_STORAGE_MIGRATION $SERVICE_STORAGE_MIGRATION;
diff --git a/build/install/docker/notify.yml b/build/install/docker/notify.yml
index 0d813e085a..7a437cf4d3 100644
--- a/build/install/docker/notify.yml
+++ b/build/install/docker/notify.yml
@@ -29,6 +29,8 @@ x-service:
- people_data:/var/www/products/ASC.People/server/
- crm_data:/var/www/products/ASC.CRM/server/
- project_data:/var/www/products/ASC.Projects/server/
+ - calendar_data:/var/www/products/ASC.Calendar/server/
+ - mail_data:/var/www/products/ASC.Mail/server/
services:
onlyoffice-notify:
@@ -47,3 +49,5 @@ volumes:
people_data:
crm_data:
project_data:
+ calendar_data:
+ mail_data:
diff --git a/packages/asc-web-components/badge/index.js b/packages/asc-web-components/badge/index.js
index e5bc04118b..4c9d0162fe 100644
--- a/packages/asc-web-components/badge/index.js
+++ b/packages/asc-web-components/badge/index.js
@@ -11,7 +11,6 @@ const Badge = (props) => {
if (!props.onClick) return;
e.preventDefault();
- e.stopPropagation();
props.onClick(e);
};
diff --git a/packages/asc-web-components/context-menu-button/index.js b/packages/asc-web-components/context-menu-button/index.js
index ed23be4ffc..5c4c585b89 100644
--- a/packages/asc-web-components/context-menu-button/index.js
+++ b/packages/asc-web-components/context-menu-button/index.js
@@ -80,8 +80,8 @@ class ContextMenuButton extends React.Component {
}
}
- onIconButtonClick = () => {
- if (this.props.isDisabled) {
+ onIconButtonClick = (e) => {
+ if (this.props.isDisabled || this.props.isNew) {
this.stopAction;
return;
}
@@ -95,7 +95,7 @@ class ContextMenuButton extends React.Component {
!this.props.isDisabled &&
this.state.isOpen &&
this.props.onClick &&
- this.props.onClick()
+ this.props.onClick(e)
); // eslint-disable-line react/prop-types
};
@@ -125,12 +125,17 @@ class ContextMenuButton extends React.Component {
}
callNewMenu = (e) => {
- if (this.props.isDisabled) {
+ if (this.props.isDisabled || !this.props.isNew) {
this.stopAction;
return;
}
- this.props.isNew && this.props.onClick(e);
+ this.setState(
+ {
+ data: this.props.getData(),
+ },
+ () => this.props.onClick(e)
+ );
};
render() {
diff --git a/products/ASC.Files/Client/src/Files.jsx b/products/ASC.Files/Client/src/Files.jsx
index 06376c70a6..82b3c9db14 100644
--- a/products/ASC.Files/Client/src/Files.jsx
+++ b/products/ASC.Files/Client/src/Files.jsx
@@ -12,9 +12,9 @@ import "./custom.scss";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
import { regDesktop } from "@appserver/common/desktop";
-import Home from "./components/pages/Home";
-import Settings from "./components/pages/Settings";
-import VersionHistory from "./components/pages/VersionHistory";
+import Home from "./pages/Home";
+import Settings from "./pages/Settings";
+import VersionHistory from "./pages/VersionHistory";
import ErrorBoundary from "@appserver/common/components/ErrorBoundary";
import Panels from "./components/FilesPanels";
import { AppServerConfig } from "@appserver/common/constants";
diff --git a/products/ASC.Files/Client/src/HOCs/withBadges.js b/products/ASC.Files/Client/src/HOCs/withBadges.js
new file mode 100644
index 0000000000..badba784b3
--- /dev/null
+++ b/products/ASC.Files/Client/src/HOCs/withBadges.js
@@ -0,0 +1,269 @@
+import React from "react";
+import { inject, observer } from "mobx-react";
+
+import {
+ ShareAccessRights,
+ AppServerConfig,
+} from "@appserver/common/constants";
+import toastr from "studio/toastr";
+import { combineUrl } from "@appserver/common/utils";
+import {
+ convertFile,
+ getFileConversationProgress,
+} from "@appserver/common/api/files";
+
+import Badges from "../components/Badges";
+import config from "../../package.json";
+
+export default function withBadges(WrappedComponent) {
+ class WithBadges extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { showConvertDialog: false };
+ }
+ onClickLock = () => {
+ const { item, lockFileAction } = this.props;
+ const { locked, id } = item;
+
+ lockFileAction(id, !locked).catch((err) => toastr.error(err));
+ };
+
+ onClickFavorite = () => {
+ const { t, item, setFavoriteAction } = this.props;
+
+ setFavoriteAction("remove", item.id)
+ .then(() => toastr.success(t("RemovedFromFavorites")))
+ .catch((err) => toastr.error(err));
+ };
+
+ onShowVersionHistory = () => {
+ const {
+ homepage,
+ isTabletView,
+ item,
+ setIsVerHistoryPanel,
+ fetchFileVersions,
+ history,
+ isTrashFolder,
+ } = this.props;
+ if (isTrashFolder) return;
+
+ if (!isTabletView) {
+ fetchFileVersions(item.id + "");
+ setIsVerHistoryPanel(true);
+ } else {
+ history.push(
+ combineUrl(AppServerConfig.proxyURL, homepage, `/${item.id}/history`)
+ );
+ }
+ };
+ onBadgeClick = () => {
+ const {
+ item,
+ selectedFolderPathParts,
+ markAsRead,
+ setNewFilesPanelVisible,
+ setNewFilesIds,
+ updateRootBadge,
+ updateFileBadge,
+ } = this.props;
+ if (item.fileExst) {
+ markAsRead([], [item.id])
+ .then(() => {
+ updateRootBadge(selectedFolderPathParts[0], 1);
+ updateFileBadge(item.id);
+ })
+ .catch((err) => toastr.error(err));
+ } else {
+ setNewFilesPanelVisible(true);
+ const newFolderIds = selectedFolderPathParts;
+ newFolderIds.push(item.id);
+ setNewFilesIds(newFolderIds);
+ }
+ };
+
+ setConvertDialogVisible = () =>
+ this.setState({ showConvertDialog: !this.state.showConvertDialog });
+
+ onConvert = () => {
+ const { item, t, setSecondaryProgressBarData } = this.props;
+ setSecondaryProgressBarData({
+ icon: "file",
+ visible: true,
+ percent: 0,
+ label: t("Convert"),
+ alert: false,
+ });
+ this.setState({ showConvertDialog: false }, () =>
+ convertFile(item.id).then((convertRes) => {
+ if (convertRes && convertRes[0] && convertRes[0].progress !== 100) {
+ this.getConvertProgress(item.id);
+ }
+ })
+ );
+ };
+
+ getConvertProgress = (fileId) => {
+ const {
+ selectedFolderId,
+ filter,
+ setIsLoading,
+ setSecondaryProgressBarData,
+ t,
+ clearSecondaryProgressData,
+ fetchFiles,
+ } = this.props;
+ getFileConversationProgress(fileId).then((res) => {
+ if (res && res[0] && res[0].progress !== 100) {
+ setSecondaryProgressBarData({
+ icon: "file",
+ visible: true,
+ percent: res[0].progress,
+ label: t("Convert"),
+ alert: false,
+ });
+ setTimeout(() => this.getConvertProgress(fileId), 1000);
+ } else {
+ if (res[0].error) {
+ setSecondaryProgressBarData({
+ visible: true,
+ alert: true,
+ });
+ toastr.error(res[0].error);
+ setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
+ } else {
+ setSecondaryProgressBarData({
+ icon: "file",
+ visible: true,
+ percent: 100,
+ label: t("Convert"),
+ alert: false,
+ });
+ setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
+ const newFilter = filter.clone();
+ fetchFiles(selectedFolderId, newFilter)
+ .catch((err) => {
+ setSecondaryProgressBarData({
+ visible: true,
+ alert: true,
+ });
+ //toastr.error(err);
+ setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
+ })
+ .finally(() => setIsLoading(false));
+ }
+ }
+ });
+ };
+ render() {
+ const { showConvertDialog } = this.state;
+ const {
+ item,
+ canWebEdit,
+ isTrashFolder,
+ canConvert,
+ onFilesClick, // from withFileAction HOC
+ } = this.props;
+ const { fileStatus, access } = item;
+
+ const newItems = item.new || fileStatus === 2;
+ const showNew = !!newItems;
+
+ const accessToEdit =
+ access === ShareAccessRights.FullAccess ||
+ access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
+
+ const badgesComponent = (
+
+ );
+
+ return (
+ <>
+ {showConvertDialog && (
+
+ )}
+
+ >
+ );
+ }
+ }
+
+ return inject(
+ (
+ {
+ auth,
+ formatsStore,
+ treeFoldersStore,
+ filesActionsStore,
+ versionHistoryStore,
+ selectedFolderStore,
+ dialogsStore,
+ filesStore,
+ uploadDataStore,
+ },
+ { item }
+ ) => {
+ const { docserviceStore } = formatsStore;
+ const { isRecycleBinFolder, updateRootBadge } = treeFoldersStore;
+ const {
+ lockFileAction,
+ setFavoriteAction,
+ markAsRead,
+ } = filesActionsStore;
+ const { isTabletView } = auth.settingsStore;
+ const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
+ const { setNewFilesPanelVisible, setNewFilesIds } = dialogsStore;
+ const { updateFileBadge, filter, setIsLoading, fetchFiles } = filesStore;
+ const { secondaryProgressDataStore } = uploadDataStore;
+ const {
+ setSecondaryProgressBarData,
+ clearSecondaryProgressData,
+ } = secondaryProgressDataStore;
+
+ const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
+ const canConvert = docserviceStore.canConvert(item.fileExst);
+
+ return {
+ canWebEdit,
+ canConvert,
+ isTrashFolder: isRecycleBinFolder,
+ lockFileAction,
+ setFavoriteAction,
+ homepage: config.homepage,
+ isTabletView,
+ setIsVerHistoryPanel,
+ fetchFileVersions,
+ selectedFolderPathParts: selectedFolderStore.pathParts,
+ markAsRead,
+ setNewFilesPanelVisible,
+ setNewFilesIds,
+ updateRootBadge,
+ updateFileBadge,
+ setSecondaryProgressBarData,
+ selectedFolderId: selectedFolderStore.id,
+ filter,
+ setIsLoading,
+ clearSecondaryProgressData,
+ fetchFiles,
+ };
+ }
+ )(observer(WithBadges));
+}
diff --git a/products/ASC.Files/Client/src/HOCs/withContent.js b/products/ASC.Files/Client/src/HOCs/withContent.js
new file mode 100644
index 0000000000..f453b200a0
--- /dev/null
+++ b/products/ASC.Files/Client/src/HOCs/withContent.js
@@ -0,0 +1,364 @@
+import React from "react";
+import { inject, observer } from "mobx-react";
+import { Trans } from "react-i18next";
+import { isMobile } from "react-device-detect";
+
+import toastr from "studio/toastr";
+import {
+ AppServerConfig,
+ FileAction,
+ ShareAccessRights,
+} from "@appserver/common/constants";
+import { combineUrl } from "@appserver/common/utils";
+
+import config from "../../package.json";
+import EditingWrapperComponent from "../components/EditingWrapperComponent";
+import { getTitleWithoutExst } from "../helpers/files-helpers";
+
+export default function withContent(WrappedContent) {
+ class WithContent extends React.Component {
+ constructor(props) {
+ super(props);
+ let titleWithoutExt = getTitleWithoutExst(props.item);
+
+ if (props.fileActionId === -1) {
+ titleWithoutExt = this.getDefaultName(props.fileActionExt);
+ }
+
+ this.state = {
+ itemTitle: titleWithoutExt,
+
+ //loading: false
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ const { fileActionId, fileActionExt } = this.props;
+ if (fileActionId === -1 && fileActionExt !== prevProps.fileActionExt) {
+ const itemTitle = this.getDefaultName(fileActionExt);
+ this.setState({ itemTitle });
+ }
+ // if (fileAction) {
+ // if (fileActionId !== prevProps.fileActionId) {
+ // this.setState({ editingId: fileActionId });
+ // }
+ // }
+ }
+
+ getDefaultName = (format) => {
+ const { t } = this.props;
+
+ switch (format) {
+ case "docx":
+ return t("NewDocument");
+ case "xlsx":
+ return t("NewSpreadsheet");
+ case "pptx":
+ return t("NewPresentation");
+ default:
+ return t("NewFolder");
+ }
+ };
+
+ completeAction = (id) => {
+ const { editCompleteAction, item } = this.props;
+
+ const isCancel =
+ (id.currentTarget && id.currentTarget.dataset.action === "cancel") ||
+ id.keyCode === 27;
+ editCompleteAction(id, item, isCancel);
+ };
+
+ updateItem = () => {
+ const {
+ t,
+ updateFile,
+ renameFolder,
+ item,
+ setIsLoading,
+ fileActionId,
+ editCompleteAction,
+ } = this.props;
+
+ const { itemTitle } = this.state;
+ const originalTitle = getTitleWithoutExst(item);
+
+ setIsLoading(true);
+ const isSameTitle =
+ originalTitle.trim() === itemTitle.trim() || itemTitle.trim() === "";
+ if (isSameTitle) {
+ this.setState({
+ itemTitle: originalTitle,
+ });
+ return editCompleteAction(fileActionId, item, isSameTitle);
+ }
+
+ item.fileExst || item.contentLength
+ ? updateFile(fileActionId, itemTitle)
+ .then(() => this.completeAction(fileActionId))
+ .then(() =>
+ toastr.success(
+ t("FileRenamed", {
+ oldTitle: item.title,
+ newTitle: itemTitle + item.fileExst,
+ })
+ )
+ )
+ .catch((err) => toastr.error(err))
+ .finally(() => setIsLoading(false))
+ : renameFolder(fileActionId, itemTitle)
+ .then(() => this.completeAction(fileActionId))
+ .then(() =>
+ toastr.success(
+ t("FolderRenamed", {
+ folderTitle: item.title,
+ newFoldedTitle: itemTitle,
+ })
+ )
+ )
+ .catch((err) => toastr.error(err))
+ .finally(() => setIsLoading(false));
+ };
+
+ cancelUpdateItem = (e) => {
+ const { item } = this.props;
+
+ const originalTitle = getTitleWithoutExst(item);
+ this.setState({
+ itemTitle: originalTitle,
+ });
+
+ return this.completeAction(e);
+ };
+
+ onClickUpdateItem = (e) => {
+ const { fileActionType } = this.props;
+
+ fileActionType === FileAction.Create
+ ? this.createItem(e)
+ : this.updateItem(e);
+ };
+
+ createItem = (e) => {
+ const {
+ createFile,
+ item,
+ setIsLoading,
+ openDocEditor,
+ isPrivacy,
+ isDesktop,
+ replaceFileStream,
+ t,
+ setEncryptionAccess,
+ createFolder,
+ } = this.props;
+ const { itemTitle } = this.state;
+
+ setIsLoading(true);
+
+ const itemId = e.currentTarget.dataset.itemid;
+
+ if (itemTitle.trim() === "") {
+ toastr.warning(t("CreateWithEmptyTitle"));
+ return this.completeAction(itemId);
+ }
+
+ let tab =
+ !isDesktop && item.fileExst
+ ? window.open(
+ combineUrl(
+ AppServerConfig.proxyURL,
+ config.homepage,
+ "/products/files/doceditor"
+ ),
+ "_blank"
+ )
+ : null;
+
+ !item.fileExst && !item.contentLength
+ ? createFolder(item.parentId, itemTitle)
+ .then(() => this.completeAction(itemId))
+ .then(() =>
+ toastr.success(
+
+ New folder {{ itemTitle }} is created
+
+ )
+ )
+ .catch((e) => toastr.error(e))
+ .finally(() => {
+ return setIsLoading(false);
+ })
+ : createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
+ .then((file) => {
+ if (isPrivacy) {
+ return setEncryptionAccess(file).then((encryptedFile) => {
+ if (!encryptedFile) return Promise.resolve();
+ toastr.info(t("EncryptedFileSaving"));
+ return replaceFileStream(
+ file.id,
+ encryptedFile,
+ true,
+ false
+ ).then(() =>
+ openDocEditor(file.id, file.providerKey, tab, file.webUrl)
+ );
+ });
+ }
+ return openDocEditor(file.id, file.providerKey, tab, file.webUrl);
+ })
+ .then(() => this.completeAction(itemId))
+ .then(() => {
+ const exst = item.fileExst;
+ return toastr.success(
+
+ New file {{ itemTitle }}.{{ exst }} is created
+
+ );
+ })
+ .catch((e) => toastr.error(e))
+ .finally(() => {
+ return setIsLoading(false);
+ });
+ };
+
+ renameTitle = (e) => {
+ const { t } = this.props;
+
+ let title = e.target.value;
+ //const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
+ const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
+ if (title.match(regexp)) {
+ toastr.warning(t("ContainsSpecCharacter"));
+ }
+ title = title.replace(regexp, "_");
+ return this.setState({ itemTitle: title });
+ };
+
+ getStatusByDate = () => {
+ const { culture, t, item, sectionWidth } = this.props;
+ const { created, updated, version, fileExst } = item;
+
+ const title =
+ version > 1
+ ? t("TitleModified")
+ : fileExst
+ ? t("TitleUploaded")
+ : t("TitleCreated");
+
+ const date = fileExst ? updated : created;
+ const dateLabel = new Date(date).toLocaleString(culture);
+ const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
+
+ return mobile ? dateLabel : `${title}: ${dateLabel}`;
+ };
+
+ render() {
+ const { itemTitle } = this.state;
+ const {
+ item,
+ fileActionId,
+ fileActionExt,
+ isLoading,
+ viewer,
+ t,
+ isTrashFolder,
+ onFilesClick,
+ } = this.props;
+ const { id, fileExst, updated, createdBy, access, fileStatus } = item;
+
+ const titleWithoutExt = getTitleWithoutExst(item);
+
+ const isEdit = id === fileActionId && fileExst === fileActionExt;
+
+ const updatedDate = updated && this.getStatusByDate();
+
+ const fileOwner =
+ createdBy &&
+ ((viewer.id === createdBy.id && t("AuthorMe")) ||
+ createdBy.displayName);
+
+ const accessToEdit =
+ access === ShareAccessRights.FullAccess || // only badges?
+ access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
+
+ const linkStyles = isTrashFolder //|| window.innerWidth <= 1024
+ ? { noHover: true }
+ : { onClick: onFilesClick };
+
+ const newItems = item.new || fileStatus === 2;
+ const showNew = !!newItems;
+
+ return isEdit ? (
+
+ ) : (
+
+ );
+ }
+ }
+
+ return inject(
+ ({ filesActionsStore, filesStore, treeFoldersStore, auth }, {}) => {
+ const { editCompleteAction } = filesActionsStore;
+ const {
+ setIsLoading,
+ openDocEditor,
+ updateFile,
+ renameFolder,
+ createFile,
+ createFolder,
+ isLoading,
+ } = filesStore;
+ const { isRecycleBinFolder, isPrivacyFolder } = treeFoldersStore;
+
+ const {
+ type: fileActionType,
+ extension: fileActionExt,
+ id: fileActionId,
+ } = filesStore.fileActionStore;
+ const { replaceFileStream, setEncryptionAccess } = auth;
+ const { culture, isDesktopClient } = auth.settingsStore;
+
+ return {
+ editCompleteAction,
+ setIsLoading,
+ isTrashFolder: isRecycleBinFolder,
+ openDocEditor,
+ updateFile,
+ renameFolder,
+ fileActionId,
+ editCompleteAction,
+ fileActionType,
+ createFile,
+ isPrivacy: isPrivacyFolder,
+ isDesktop: isDesktopClient,
+ replaceFileStream,
+ setEncryptionAccess,
+ createFolder,
+ fileActionExt,
+ isLoading,
+ culture,
+ homepage: config.homepage,
+ viewer: auth.userStore.user,
+ };
+ }
+ )(observer(WithContent));
+}
diff --git a/products/ASC.Files/Client/src/HOCs/withContextOptions.js b/products/ASC.Files/Client/src/HOCs/withContextOptions.js
new file mode 100644
index 0000000000..8ec66f6c33
--- /dev/null
+++ b/products/ASC.Files/Client/src/HOCs/withContextOptions.js
@@ -0,0 +1,503 @@
+import React from "react";
+import { inject, observer } from "mobx-react";
+import copy from "copy-to-clipboard";
+
+import { combineUrl } from "@appserver/common/utils";
+import { FileAction, AppServerConfig } from "@appserver/common/constants";
+import toastr from "studio/toastr";
+
+import config from "../../package.json";
+
+export default function withContextOptions(WrappedComponent) {
+ class WithContextOptions extends React.Component {
+ onOpenLocation = () => {
+ const { item, openLocationAction } = this.props;
+ const { id, folderId, fileExst } = item;
+
+ const locationId = !fileExst ? id : folderId;
+ openLocationAction(locationId, !fileExst);
+ };
+
+ onOwnerChange = () => {
+ const { setChangeOwnerPanelVisible } = this.props;
+ setChangeOwnerPanelVisible(true);
+ };
+ onMoveAction = () => {
+ const { setMoveToPanelVisible } = this.props;
+ setMoveToPanelVisible(true);
+ };
+ onCopyAction = () => {
+ const { setCopyPanelVisible } = this.props;
+ setCopyPanelVisible(true);
+ };
+
+ showVersionHistory = () => {
+ const {
+ item,
+ isTabletView,
+ fetchFileVersions,
+ setIsVerHistoryPanel,
+ history,
+ homepage,
+ isTrashFolder,
+ } = this.props;
+ const { id } = item;
+ if (isTrashFolder) return;
+
+ if (!isTabletView) {
+ fetchFileVersions(id + "");
+ setIsVerHistoryPanel(true);
+ } else {
+ history.push(
+ combineUrl(AppServerConfig.proxyURL, homepage, `/${id}/history`)
+ );
+ }
+ };
+
+ finalizeVersion = () => {
+ const { item, finalizeVersionAction } = this.props;
+ const { id } = item;
+ finalizeVersionAction(id).catch((err) => toastr.error(err));
+ };
+
+ onClickFavorite = (e) => {
+ const { item, setFavoriteAction, t } = this.props;
+ const { id } = item;
+ const data = (e.currentTarget && e.currentTarget.dataset) || e;
+ const { action } = data;
+
+ setFavoriteAction(action, id)
+ .then(() =>
+ action === "mark"
+ ? toastr.success(t("MarkedAsFavorite"))
+ : toastr.success(t("RemovedFromFavorites"))
+ )
+ .catch((err) => toastr.error(err));
+ };
+
+ lockFile = () => {
+ const { item, lockFileAction } = this.props;
+ const { id, locked } = item;
+ lockFileAction(id, !locked).catch((err) => toastr.error(err));
+ };
+
+ onClickLinkForPortal = () => {
+ const { item, homepage, t } = this.props;
+ const { fileExst, canOpenPlayer, webUrl } = item;
+
+ const isFile = !!fileExst;
+ copy(
+ isFile
+ ? canOpenPlayer
+ ? `${window.location.href}&preview=${id}`
+ : webUrl
+ : `${window.location.origin + homepage}/filter?folder=${id}`
+ );
+
+ toastr.success(t("LinkCopySuccess"));
+ };
+
+ onClickLinkEdit = () => {
+ const { item, openDocEditor } = this.props;
+ const { id, providerKey } = item;
+ openDocEditor(id, providerKey);
+ };
+
+ onClickDownload = () => {
+ const { item, downloadAction, t } = this.props;
+ const { fileExst, contentLength, viewUrl } = item;
+ const isFile = !!fileExst && contentLength;
+ isFile
+ ? window.open(viewUrl, "_blank")
+ : downloadAction(t("ArchivingData")).catch((err) => toastr.error(err));
+ };
+
+ onClickDownloadAs = () => {
+ const { setDownloadDialogVisible } = this.props;
+ setDownloadDialogVisible(true);
+ };
+
+ onDuplicate = () => {
+ const { duplicateAction, t, item } = this.props;
+ duplicateAction(item, t("CopyOperation")).catch((err) =>
+ toastr.error(err)
+ );
+ };
+
+ onClickRename = () => {
+ const { item, setAction } = this.props;
+ const { id, fileExst } = item;
+ setAction({
+ type: FileAction.Rename,
+ extension: fileExst,
+ id,
+ });
+ };
+
+ onChangeThirdPartyInfo = () => {
+ const { item, setThirdpartyInfo } = this.props;
+ const { providerKey } = item;
+ setThirdpartyInfo(providerKey);
+ };
+
+ onMediaFileClick = (fileId) => {
+ const { item, setMediaViewerData } = this.props;
+ const itemId = typeof fileId !== "object" ? fileId : item.id;
+ setMediaViewerData({ visible: true, id: itemId });
+ };
+
+ onClickDelete = () => {
+ const {
+ item,
+ setRemoveItem,
+ setDeleteThirdPartyDialogVisible,
+ confirmDelete,
+ setDeleteDialogVisible,
+ t,
+ deleteFileAction,
+ deleteFolderAction,
+ isThirdPartyFolder,
+ } = this.props;
+ const { id, title, fileExst, contentLength, folderId, parentId } = item;
+
+ if (isThirdPartyFolder) {
+ const splitItem = id.split("-");
+ setRemoveItem({ id: splitItem[splitItem.length - 1], title });
+ setDeleteThirdPartyDialogVisible(true);
+ return;
+ }
+
+ if (confirmDelete) {
+ setDeleteDialogVisible(true);
+ } else {
+ const translations = {
+ deleteOperation: t("DeleteOperation"),
+ };
+
+ fileExst || contentLength
+ ? deleteFileAction(id, folderId, translations)
+ .then(() => toastr.success(t("FileRemoved")))
+ .catch((err) => toastr.error(err))
+ : deleteFolderAction(id, parentId, translations)
+ .then(() => toastr.success(t("FolderRemoved")))
+ .catch((err) => toastr.error(err));
+ }
+ };
+
+ onClickShare = () => {
+ const { onSelectItem, setSharingPanelVisible, item } = this.props;
+ onSelectItem(item);
+ setSharingPanelVisible(true);
+ };
+
+ getFilesContextOptions = () => {
+ const { item, t, isThirdPartyFolder } = this.props;
+ const { access, contextOptions } = item;
+ const isSharable = access !== 1 && access !== 0;
+ return contextOptions.map((option) => {
+ switch (option) {
+ case "open":
+ return {
+ key: option,
+ label: t("Open"),
+ icon: "images/catalog.folder.react.svg",
+ onClick: this.onOpenLocation,
+ disabled: false,
+ };
+ case "show-version-history":
+ return {
+ key: option,
+ label: t("ShowVersionHistory"),
+ icon: "images/history.react.svg",
+ onClick: this.showVersionHistory,
+ disabled: false,
+ };
+ case "finalize-version":
+ return {
+ key: option,
+ label: t("FinalizeVersion"),
+ icon: "images/history-finalized.react.svg",
+ onClick: this.finalizeVersion,
+ disabled: false,
+ };
+ case "separator0":
+ case "separator1":
+ case "separator2":
+ case "separator3":
+ return { key: option, isSeparator: true };
+ case "open-location":
+ return {
+ key: option,
+ label: t("OpenLocation"),
+ icon: "images/download-as.react.svg",
+ onClick: this.onOpenLocation,
+ disabled: false,
+ };
+ case "mark-as-favorite":
+ return {
+ key: option,
+ label: t("MarkAsFavorite"),
+ icon: "images/favorites.react.svg",
+ onClick: this.onClickFavorite,
+ disabled: false,
+ "data-action": "mark",
+ action: "mark",
+ };
+ case "block-unblock-version":
+ return {
+ key: option,
+ label: t("UnblockVersion"),
+ icon: "images/lock.react.svg",
+ onClick: this.lockFile,
+ disabled: false,
+ };
+ case "sharing-settings":
+ return {
+ key: option,
+ label: t("SharingSettings"),
+ icon: "images/catalog.shared.react.svg",
+ onClick: this.onClickShare,
+ disabled: isSharable,
+ };
+ case "send-by-email":
+ return {
+ key: option,
+ label: t("SendByEmail"),
+ icon: "/static/images/mail.react.svg",
+ disabled: true,
+ };
+ case "owner-change":
+ return {
+ key: option,
+ label: t("ChangeOwner"),
+ icon: "images/catalog.user.react.svg",
+ onClick: this.onOwnerChange,
+ disabled: false,
+ };
+ case "link-for-portal-users":
+ return {
+ key: option,
+ label: t("LinkForPortalUsers"),
+ icon: "/static/images/invitation.link.react.svg",
+ onClick: this.onClickLinkForPortal,
+ disabled: false,
+ };
+ case "edit":
+ return {
+ key: option,
+ label: t("Edit"),
+ icon: "/static/images/access.edit.react.svg",
+ onClick: this.onClickLinkEdit,
+ disabled: false,
+ };
+ case "preview":
+ return {
+ key: option,
+ label: t("Preview"),
+ icon: "EyeIcon",
+ onClick: this.onClickLinkEdit,
+ disabled: true,
+ };
+ case "view":
+ return {
+ key: option,
+ label: t("View"),
+ icon: "/static/images/eye.react.svg",
+ onClick: this.onMediaFileClick,
+ disabled: false,
+ };
+ case "download":
+ return {
+ key: option,
+ label: t("Download"),
+ icon: "images/download.react.svg",
+ onClick: this.onClickDownload,
+ disabled: false,
+ };
+ case "download-as":
+ return {
+ key: option,
+ label: t("DownloadAs"),
+ icon: "images/download-as.react.svg",
+ onClick: this.onClickDownloadAs,
+ disabled: false,
+ };
+ case "move-to":
+ return {
+ key: option,
+ label: t("MoveTo"),
+ icon: "images/move.react.svg",
+ onClick: this.onMoveAction,
+ disabled: false,
+ };
+ case "restore":
+ return {
+ key: option,
+ label: t("Restore"),
+ icon: "images/move.react.svg",
+ onClick: this.onMoveAction,
+ disabled: false,
+ };
+ case "copy-to":
+ return {
+ key: option,
+ label: t("Copy"),
+ icon: "/static/images/copy.react.svg",
+ onClick: this.onCopyAction,
+ disabled: false,
+ };
+ case "copy":
+ return {
+ key: option,
+ label: t("Duplicate"),
+ icon: "/static/images/copy.react.svg",
+ onClick: this.onDuplicate,
+ disabled: false,
+ };
+ case "rename":
+ return {
+ key: option,
+ label: t("Rename"),
+ icon: "images/rename.react.svg",
+ onClick: this.onClickRename,
+ disabled: false,
+ };
+ case "change-thirdparty-info":
+ return {
+ key: option,
+ label: t("ThirdPartyInfo"),
+ icon: "/static/images/access.edit.react.svg",
+ onClick: this.onChangeThirdPartyInfo,
+ disabled: false,
+ };
+ case "delete":
+ return {
+ key: option,
+ label: isThirdPartyFolder ? t("DeleteThirdParty") : t("Delete"),
+ icon: "/static/images/catalog.trash.react.svg",
+ onClick: this.onClickDelete,
+ disabled: false,
+ };
+ case "remove-from-favorites":
+ return {
+ key: option,
+ label: t("RemoveFromFavorites"),
+ icon: "images/favorites.react.svg",
+ onClick: this.onClickFavorite,
+ disabled: false,
+ "data-action": "remove",
+ action: "remove",
+ };
+ default:
+ break;
+ }
+
+ return undefined;
+ });
+ };
+ render() {
+ const { actionType, actionId, actionExtension, item } = this.props;
+ const { id, fileExst, contextOptions } = item;
+
+ const isEdit =
+ !!actionType && actionId === id && fileExst === actionExtension;
+
+ const contextOptionsProps =
+ !isEdit && contextOptions && contextOptions.length > 0
+ ? {
+ contextOptions: this.getFilesContextOptions(),
+ }
+ : {};
+
+ return (
+
+ );
+ }
+ }
+
+ return inject(
+ (
+ {
+ filesStore,
+ filesActionsStore,
+ auth,
+ versionHistoryStore,
+ mediaViewerDataStore,
+ settingsStore,
+ selectedFolderStore,
+ dialogsStore,
+ treeFoldersStore,
+ },
+ { item }
+ ) => {
+ const { openDocEditor, fileActionStore } = filesStore;
+ const {
+ openLocationAction,
+ finalizeVersionAction,
+ setFavoriteAction,
+ lockFileAction,
+ downloadAction,
+ duplicateAction,
+ setThirdpartyInfo,
+ deleteFileAction,
+ deleteFolderAction,
+ onSelectItem,
+ } = filesActionsStore;
+ const {
+ setChangeOwnerPanelVisible,
+ setMoveToPanelVisible,
+ setCopyPanelVisible,
+ setDownloadDialogVisible,
+ setRemoveItem,
+ setDeleteThirdPartyDialogVisible,
+ setDeleteDialogVisible,
+ setSharingPanelVisible,
+ } = dialogsStore;
+ const { isTabletView } = auth.settingsStore;
+ const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
+ const { setAction, type, extension, id } = fileActionStore;
+ const { setMediaViewerData } = mediaViewerDataStore;
+ const { isRootFolder } = selectedFolderStore;
+ const { isRecycleBinFolder } = treeFoldersStore;
+
+ const isThirdPartyFolder = item.providerKey && isRootFolder;
+
+ return {
+ openLocationAction,
+ setChangeOwnerPanelVisible,
+ setMoveToPanelVisible,
+ setCopyPanelVisible,
+ isTabletView,
+ setIsVerHistoryPanel,
+ fetchFileVersions,
+ homepage: config.homepage,
+ finalizeVersionAction,
+ setFavoriteAction,
+ lockFileAction,
+ openDocEditor,
+ downloadAction,
+ setDownloadDialogVisible,
+ duplicateAction,
+ setAction,
+ setThirdpartyInfo,
+ setMediaViewerData,
+ setRemoveItem,
+ setDeleteThirdPartyDialogVisible,
+ confirmDelete: settingsStore.confirmDelete,
+ setDeleteDialogVisible,
+ deleteFileAction,
+ deleteFolderAction,
+ isThirdPartyFolder,
+ onSelectItem,
+ setSharingPanelVisible,
+ actionType: type,
+ actionId: id,
+ actionExtension: extension,
+ isTrashFolder: isRecycleBinFolder,
+ };
+ }
+ )(observer(WithContextOptions));
+}
diff --git a/products/ASC.Files/Client/src/HOCs/withFileActions.js b/products/ASC.Files/Client/src/HOCs/withFileActions.js
new file mode 100644
index 0000000000..08f5d8f870
--- /dev/null
+++ b/products/ASC.Files/Client/src/HOCs/withFileActions.js
@@ -0,0 +1,339 @@
+import React from "react";
+import { inject, observer } from "mobx-react";
+import { ReactSVG } from "react-svg";
+
+import IconButton from "@appserver/components/icon-button";
+import Text from "@appserver/components/text";
+
+import { EncryptedFileIcon } from "../components/Icons";
+
+const svgLoader = () =>
;
+export default function withFileActions(WrappedFileItem) {
+ class WithFileActions extends React.Component {
+ onContentRowSelect = (checked, file) => {
+ const { selectRowAction } = this.props;
+ if (!file) return;
+ selectRowAction(checked, file);
+ };
+
+ onClickShare = () => {
+ const { onSelectItem, setSharingPanelVisible, item } = this.props;
+ onSelectItem(item);
+ setSharingPanelVisible(true);
+ };
+
+ rowContextClick = () => {
+ const { onSelectItem, item } = this.props;
+ onSelectItem(item);
+ };
+
+ getSharedButton = (shared) => {
+ const { t } = this.props;
+ const color = shared ? "#657077" : "#a3a9ae";
+ return (
+
+
+ {t("Share")}
+
+ );
+ };
+
+ getItemIcon = (isEdit) => {
+ const { item, isPrivacy } = this.props;
+ const { icon, fileExst } = item;
+ return (
+ <>
+
+ {isPrivacy && fileExst && }
+ >
+ );
+ };
+
+ onDropZoneUpload = (files, uploadToFolder) => {
+ const {
+ selectedFolderId,
+ dragging,
+ setDragging,
+ startUpload,
+ } = this.props;
+
+ const folderId = uploadToFolder ? uploadToFolder : selectedFolderId;
+ dragging && setDragging(false);
+ startUpload(files, folderId, t);
+ };
+
+ onDrop = (items) => {
+ const { item, selectedFolderId } = this.props;
+ const { fileExst, id } = item;
+
+ if (!fileExst) {
+ this.onDropZoneUpload(items, id);
+ } else {
+ this.onDropZoneUpload(items, selectedFolderId);
+ }
+ };
+
+ onMouseDown = (e) => {
+ const { draggable, setTooltipPosition, setStartDrag } = this.props;
+ if (!draggable) {
+ return;
+ }
+
+ if (
+ window.innerWidth < 1025 ||
+ e.target.tagName === "rect" ||
+ e.target.tagName === "path"
+ ) {
+ return;
+ }
+ const mouseButton = e.which
+ ? e.which !== 1
+ : e.button
+ ? e.button !== 0
+ : false;
+ const label = e.currentTarget.getAttribute("label");
+ if (mouseButton || e.currentTarget.tagName !== "DIV" || label) {
+ return;
+ }
+
+ setTooltipPosition(e.pageX, e.pageY);
+ setStartDrag(true);
+ };
+
+ onFilesClick = () => {
+ const {
+ filter,
+ parentFolder,
+ setIsLoading,
+ fetchFiles,
+ isImage,
+ isSound,
+ isVideo,
+ canWebEdit,
+ item,
+ isTrashFolder,
+ openDocEditor,
+ expandedKeys,
+ addExpandedKeys,
+ setMediaViewerData,
+ } = this.props;
+ const { id, fileExst, viewUrl, providerKey, contentLength } = item;
+
+ if (isTrashFolder) return;
+
+ if (!fileExst && !contentLength) {
+ setIsLoading(true);
+
+ if (!expandedKeys.includes(parentFolder + "")) {
+ addExpandedKeys(parentFolder + "");
+ }
+
+ fetchFiles(id, filter)
+ .catch((err) => {
+ toastr.error(err);
+ setIsLoading(false);
+ })
+ .finally(() => setIsLoading(false));
+ } else {
+ if (canWebEdit) {
+ return openDocEditor(id, providerKey);
+ }
+
+ if (isImage || isSound || isVideo) {
+ setMediaViewerData({ visible: true, id });
+ return;
+ }
+
+ return window.open(viewUrl, "_blank");
+ }
+ };
+
+ render() {
+ const {
+ item,
+ isRecycleBin,
+ draggable,
+ canShare,
+ isPrivacy,
+ actionType,
+ actionExtension,
+ actionId,
+ sectionWidth,
+ checked,
+ dragging,
+ isFolder,
+ } = this.props;
+ const { fileExst, access, contentLength, id, shared } = item;
+
+ const isEdit =
+ !!actionType && actionId === id && fileExst === actionExtension;
+
+ const isDragging = isFolder && access < 2 && !isRecycleBin;
+
+ let className = isDragging ? " droppable" : "";
+ if (draggable) className += " draggable not-selectable";
+
+ let value = fileExst || contentLength ? `file_${id}` : `folder_${id}`;
+ value += draggable ? "_draggable" : "";
+
+ const isMobile = sectionWidth < 500;
+ const displayShareButton = isMobile
+ ? "26px"
+ : !canShare
+ ? "38px"
+ : "96px";
+
+ const sharedButton =
+ !canShare || (isPrivacy && !fileExst) || isEdit || id <= 0 || isMobile
+ ? null
+ : this.getSharedButton(shared);
+
+ const checkedProps = isEdit || id <= 0 ? {} : { checked };
+ const element = this.getItemIcon(isEdit || id <= 0);
+
+ return (
+
+ );
+ }
+ }
+
+ return inject(
+ (
+ {
+ filesActionsStore,
+ dialogsStore,
+ treeFoldersStore,
+ selectedFolderStore,
+ filesStore,
+ uploadDataStore,
+ formatsStore,
+ mediaViewerDataStore,
+ },
+ { item, t, history }
+ ) => {
+ const { selectRowAction, onSelectItem } = filesActionsStore;
+ const { setSharingPanelVisible } = dialogsStore;
+ const {
+ isPrivacyFolder,
+ isRecycleBinFolder,
+ expandedKeys,
+ addExpandedKeys,
+ } = treeFoldersStore;
+ const { id: selectedFolderId, isRootFolder } = selectedFolderStore;
+ const {
+ dragging,
+ setDragging,
+ selection,
+ setTooltipPosition,
+ setStartDrag,
+ fileActionStore,
+ canShare,
+ isFileSelected,
+ filter,
+ setIsLoading,
+ fetchFiles,
+ openDocEditor,
+ } = filesStore;
+ const { startUpload } = uploadDataStore;
+ const { type, extension, id } = fileActionStore;
+ const {
+ iconFormatsStore,
+ mediaViewersFormatsStore,
+ docserviceStore,
+ } = formatsStore;
+ const { setMediaViewerData } = mediaViewerDataStore;
+
+ const selectedItem = selection.find(
+ (x) => x.id === item.id && x.fileExst === item.fileExst
+ );
+
+ const draggable =
+ !isRecycleBinFolder && selectedItem && selectedItem.id !== id;
+
+ const isFolder = selectedItem
+ ? false
+ : item.fileExst //|| item.contentLength
+ ? false
+ : true;
+
+ const isImage = iconFormatsStore.isImage(item.fileExst);
+ const isSound = iconFormatsStore.isSound(item.fileExst);
+ const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
+ const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
+
+ return {
+ t,
+ item,
+ selectRowAction,
+ onSelectItem,
+ setSharingPanelVisible,
+ isPrivacy: isPrivacyFolder,
+ selectedFolderId,
+ dragging,
+ setDragging,
+ startUpload,
+ draggable,
+ setTooltipPosition,
+ setStartDrag,
+ history,
+ isFolder,
+ isRootFolder,
+ canShare,
+ actionType: type,
+ actionExtension: extension,
+ actionId: id,
+ checked: isFileSelected(item.id, item.parentId),
+ filter,
+ parentFolder: selectedFolderStore.parentId,
+ setIsLoading,
+ fetchFiles,
+ isImage,
+ isSound,
+ isVideo,
+ canWebEdit,
+ isTrashFolder: isRecycleBinFolder,
+ openDocEditor,
+ expandedKeys,
+ addExpandedKeys,
+ setMediaViewerData,
+ };
+ }
+ )(observer(WithFileActions));
+}
diff --git a/products/ASC.Files/Client/src/HOCs/withLoader.js b/products/ASC.Files/Client/src/HOCs/withLoader.js
new file mode 100644
index 0000000000..669617e745
--- /dev/null
+++ b/products/ASC.Files/Client/src/HOCs/withLoader.js
@@ -0,0 +1,51 @@
+import React, { useEffect, useState } from "react";
+import { observer, inject } from "mobx-react";
+import { isMobile } from "react-device-detect";
+
+import Loaders from "@appserver/common/components/Loaders";
+
+let loadTimeout = null;
+export default function withLoader(WrappedComponent, type) {
+ const withLoader = (props) => {
+ const { tReady, firstLoad, isLoaded, isLoading } = props;
+ const [inLoad, setInLoad] = useState(false);
+
+ const cleanTimer = () => {
+ loadTimeout && clearTimeout(loadTimeout);
+ loadTimeout = null;
+ };
+
+ useEffect(() => {
+ if (isLoading) {
+ cleanTimer();
+ loadTimeout = setTimeout(() => {
+ console.log("inLoad", true);
+ setInLoad(true);
+ }, 500);
+ } else {
+ cleanTimer();
+ console.log("inLoad", false);
+ setInLoad(false);
+ }
+
+ return () => {
+ cleanTimer();
+ };
+ }, [isLoading]);
+
+ return firstLoad || !isLoaded || (isMobile && inLoad) || !tReady ? (
+
+ ) : (
+
+ );
+ };
+
+ return inject(({ auth, filesStore }) => {
+ const { firstLoad, isLoading } = filesStore;
+ return {
+ firstLoad,
+ isLoaded: auth.isLoaded,
+ isLoading,
+ };
+ })(observer(withLoader));
+}
diff --git a/products/ASC.Files/Client/src/components/Badges.js b/products/ASC.Files/Client/src/components/Badges.js
new file mode 100644
index 0000000000..6e18e4ea52
--- /dev/null
+++ b/products/ASC.Files/Client/src/components/Badges.js
@@ -0,0 +1,123 @@
+import React from "react";
+import Badge from "@appserver/components/badge";
+import IconButton from "@appserver/components/icon-button";
+import {
+ StyledFavoriteIcon,
+ StyledFileActionsConvertEditDocIcon,
+ StyledFileActionsLockedIcon,
+} from "./Icons";
+
+const Badges = ({
+ newItems,
+ item,
+ canWebEdit,
+ isTrashFolder,
+ /* canConvert, */
+ accessToEdit,
+ showNew,
+ onFilesClick,
+ onClickLock,
+ onClickFavorite,
+ onShowVersionHistory,
+ onBadgeClick,
+ /*setConvertDialogVisible*/
+}) => {
+ const { id, locked, fileStatus, versionGroup, title, fileExst } = item;
+
+ return fileExst ? (
+
+ {/* TODO: Uncomment after fix conversation {canConvert && !isTrashFolder && (
+
+ )} */}
+ {canWebEdit && !isTrashFolder && accessToEdit && (
+
+ )}
+ {locked && (
+
+ )}
+ {fileStatus === 32 && !isTrashFolder && (
+
+ )}
+ {fileStatus === 1 && (
+
+ )}
+ {versionGroup > 1 && (
+
+ )}
+ {showNew && (
+
+ )}
+
+ ) : (
+ showNew && (
+
+ )
+ );
+};
+
+export default Badges;
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EditingWrapperComponent.js b/products/ASC.Files/Client/src/components/EditingWrapperComponent.js
similarity index 81%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EditingWrapperComponent.js
rename to products/ASC.Files/Client/src/components/EditingWrapperComponent.js
index 20542112db..6ac5d79df7 100644
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EditingWrapperComponent.js
+++ b/products/ASC.Files/Client/src/components/EditingWrapperComponent.js
@@ -2,6 +2,35 @@ import React, { useState } from "react";
import styled from "styled-components";
import Button from "@appserver/components/button";
import TextInput from "@appserver/components/text-input";
+import commonIconsStyles from "@appserver/components/utils/common-icons-style";
+
+import CheckIcon from "../../public/images/check.react.svg";
+import CrossIcon from "../../../../../public/images/cross.react.svg";
+
+const StyledCheckIcon = styled(CheckIcon)`
+ ${commonIconsStyles}
+ path {
+ fill: #a3a9ae;
+ }
+ :hover {
+ fill: #657077;
+ }
+`;
+
+const StyledCrossIcon = styled(CrossIcon)`
+ ${commonIconsStyles}
+ path {
+ fill: #a3a9ae;
+ }
+ :hover {
+ fill: #657077;
+ }
+`;
+
+export const okIcon = ;
+export const cancelIcon = (
+
+);
const EditingWrapper = styled.div`
width: 100%;
@@ -49,8 +78,6 @@ const EditingWrapperComponent = (props) => {
const {
itemTitle,
itemId,
- okIcon,
- cancelIcon,
renameTitle,
onClickUpdateItem,
cancelUpdateItem,
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyContainer.js b/products/ASC.Files/Client/src/components/EmptyContainer/EmptyContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyContainer.js
rename to products/ASC.Files/Client/src/components/EmptyContainer/EmptyContainer.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyFilterContainer.js b/products/ASC.Files/Client/src/components/EmptyContainer/EmptyFilterContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyFilterContainer.js
rename to products/ASC.Files/Client/src/components/EmptyContainer/EmptyFilterContainer.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyFolderContainer.js b/products/ASC.Files/Client/src/components/EmptyContainer/EmptyFolderContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/EmptyFolderContainer.js
rename to products/ASC.Files/Client/src/components/EmptyContainer/EmptyFolderContainer.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/RootFolderContainer.js b/products/ASC.Files/Client/src/components/EmptyContainer/RootFolderContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/RootFolderContainer.js
rename to products/ASC.Files/Client/src/components/EmptyContainer/RootFolderContainer.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/index.js b/products/ASC.Files/Client/src/components/EmptyContainer/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/EmptyContainer/index.js
rename to products/ASC.Files/Client/src/components/EmptyContainer/index.js
diff --git a/products/ASC.Files/Client/src/components/Icons.js b/products/ASC.Files/Client/src/components/Icons.js
new file mode 100644
index 0000000000..3e9538e4bf
--- /dev/null
+++ b/products/ASC.Files/Client/src/components/Icons.js
@@ -0,0 +1,36 @@
+import styled from "styled-components";
+
+import commonIconsStyles from "@appserver/components/utils/common-icons-style";
+
+import FavoriteIcon from "../../public/images/favorite.react.svg";
+import FileActionsConvertEditDocIcon from "../../public/images/file.actions.convert.edit.doc.react.svg";
+import FileActionsLockedIcon from "../../public/images/file.actions.locked.react.svg";
+
+export const EncryptedFileIcon = styled.div`
+ background: url("images/security.svg") no-repeat 0 0 / 16px 16px transparent;
+ height: 16px;
+ position: absolute;
+ width: 16px;
+ margin-top: 14px;
+ margin-left: ${(props) => (props.isEdit ? "40px" : "12px")};
+`;
+
+export const StyledFavoriteIcon = styled(FavoriteIcon)`
+ ${commonIconsStyles}
+`;
+
+export const StyledFileActionsConvertEditDocIcon = styled(
+ FileActionsConvertEditDocIcon
+)`
+ ${commonIconsStyles}
+ path {
+ fill: #3b72a7;
+ }
+`;
+
+export const StyledFileActionsLockedIcon = styled(FileActionsLockedIcon)`
+ ${commonIconsStyles}
+ path {
+ fill: #3b72a7;
+ }
+`;
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContainer.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContainer.js
deleted file mode 100644
index c63c072e9e..0000000000
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContainer.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { inject, observer } from "mobx-react";
-import RowContainer from "@appserver/components/row-container";
-import { Consumer } from "@appserver/components/utils/context";
-import SimpleFilesRow from "./SimpleFilesRow";
-import Loaders from "@appserver/common/components/Loaders";
-import { isMobile } from "react-device-detect";
-
-let loadTimeout = null;
-
-const FilesRowContainer = ({ isLoaded, isLoading, filesList, tReady }) => {
- const [inLoad, setInLoad] = useState(false);
-
- const cleanTimer = () => {
- loadTimeout && clearTimeout(loadTimeout);
- loadTimeout = null;
- };
-
- useEffect(() => {
- if (isLoading) {
- cleanTimer();
- loadTimeout = setTimeout(() => {
- console.log("inLoad", true);
- setInLoad(true);
- }, 500);
- } else {
- cleanTimer();
- console.log("inLoad", false);
- setInLoad(false);
- }
-
- return () => {
- cleanTimer();
- };
- }, [isLoading]);
-
- return !isLoaded || (isMobile && inLoad) || !tReady ? (
-
- ) : (
-
- {(context) => (
-
- {filesList.map((item, index) => (
-
- ))}
-
- )}
-
- );
-};
-
-export default inject(({ auth, filesStore }) => {
- const { filesList, isLoading } = filesStore;
-
- return {
- filesList,
- isLoading,
- isLoaded: auth.isLoaded,
- };
-})(observer(FilesRowContainer));
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContent.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContent.js
deleted file mode 100644
index d6f9cc7ffe..0000000000
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/FilesRowContent.js
+++ /dev/null
@@ -1,902 +0,0 @@
-import React from "react";
-import { withRouter } from "react-router";
-import { Trans, withTranslation } from "react-i18next";
-import styled from "styled-components";
-import Link from "@appserver/components/link";
-import Text from "@appserver/components/text";
-import RowContent from "@appserver/components/row-content";
-import IconButton from "@appserver/components/icon-button";
-import Badge from "@appserver/components/badge";
-import commonIconsStyles from "@appserver/components/utils/common-icons-style";
-import {
- convertFile,
- getFileConversationProgress,
-} from "@appserver/common/api/files";
-import {
- AppServerConfig,
- FileAction,
- ShareAccessRights,
-} from "@appserver/common/constants";
-import toastr from "studio/toastr";
-import FavoriteIcon from "../../../../../../../public/images/favorite.react.svg";
-import FileActionsConvertEditDocIcon from "../../../../../../../public/images/file.actions.convert.edit.doc.react.svg";
-import FileActionsLockedIcon from "../../../../../../../public/images/file.actions.locked.react.svg";
-import CheckIcon from "../../../../../../../public/images/check.react.svg";
-import CrossIcon from "../../../../../../../../../../public/images/cross.react.svg";
-import { TIMEOUT } from "../../../../../../helpers/constants";
-import { getTitleWithoutExst } from "../../../../../../helpers/files-helpers";
-import { ConvertDialog } from "../../../../../dialogs";
-import EditingWrapperComponent from "../EditingWrapperComponent";
-import { isMobile } from "react-device-detect";
-import { observer, inject } from "mobx-react";
-import config from "../../../../../../../package.json";
-import { combineUrl } from "@appserver/common/utils";
-
-const sideColor = "#A3A9AE";
-const StyledCheckIcon = styled(CheckIcon)`
- ${commonIconsStyles}
- path {
- fill: #a3a9ae;
- }
- :hover {
- fill: #657077;
- }
-`;
-
-const StyledCrossIcon = styled(CrossIcon)`
- ${commonIconsStyles}
- path {
- fill: #a3a9ae;
- }
- :hover {
- fill: #657077;
- }
-`;
-
-const StyledFavoriteIcon = styled(FavoriteIcon)`
- ${commonIconsStyles}
-`;
-
-const StyledFileActionsConvertEditDocIcon = styled(
- FileActionsConvertEditDocIcon
-)`
- ${commonIconsStyles}
- path {
- fill: #3b72a7;
- }
-`;
-
-const StyledFileActionsLockedIcon = styled(FileActionsLockedIcon)`
- ${commonIconsStyles}
- path {
- fill: #3b72a7;
- }
-`;
-const SimpleFilesRowContent = styled(RowContent)`
- .badge-ext {
- margin-left: -8px;
- margin-right: 8px;
- }
-
- .badge {
- height: 14px;
- width: 14px;
- margin-right: 6px;
- }
- .lock-file {
- cursor: pointer;
- }
- .badges {
- display: flex;
- align-items: center;
- }
-
- .favorite {
- cursor: pointer;
- margin-right: 6px;
- }
-
- .share-icon {
- margin-top: -4px;
- padding-right: 8px;
- }
-
- .row_update-text {
- overflow: hidden;
- text-overflow: ellipsis;
- }
-`;
-
-const okIcon = ;
-
-const cancelIcon = (
-
-);
-
-class FilesRowContent extends React.PureComponent {
- constructor(props) {
- super(props);
- let titleWithoutExt = getTitleWithoutExst(props.item);
-
- if (props.fileActionId === -1) {
- titleWithoutExt = this.getDefaultName(props.fileActionExt);
- }
-
- this.state = {
- itemTitle: titleWithoutExt,
- showConvertDialog: false,
- //loading: false
- };
- }
-
- completeAction = (id) => {
- const isCancel =
- (id.currentTarget && id.currentTarget.dataset.action === "cancel") ||
- id.keyCode === 27;
- this.props.editCompleteAction(id, this.props.item, isCancel);
- };
-
- updateItem = () => {
- const {
- t,
- updateFile,
- renameFolder,
- item,
- setIsLoading,
- fileActionId,
- editCompleteAction,
- } = this.props;
-
- const { itemTitle } = this.state;
- const originalTitle = getTitleWithoutExst(item);
-
- setIsLoading(true);
- const isSameTitle =
- originalTitle.trim() === itemTitle.trim() || itemTitle.trim() === "";
- if (isSameTitle) {
- this.setState({
- itemTitle: originalTitle,
- });
- return editCompleteAction(fileActionId, item, isSameTitle);
- }
-
- item.fileExst || item.contentLength
- ? updateFile(fileActionId, itemTitle)
- .then(() => this.completeAction(fileActionId))
- .then(() =>
- toastr.success(
- t("FileRenamed", {
- oldTitle: item.title,
- newTitle: itemTitle + item.fileExst,
- })
- )
- )
- .catch((err) => toastr.error(err))
- .finally(() => setIsLoading(false))
- : renameFolder(fileActionId, itemTitle)
- .then(() => this.completeAction(fileActionId))
- .then(() =>
- toastr.success(
- t("FolderRenamed", {
- folderTitle: item.title,
- newFoldedTitle: itemTitle,
- })
- )
- )
- .catch((err) => toastr.error(err))
- .finally(() => setIsLoading(false));
- };
-
- createItem = (e) => {
- const {
- createFile,
- item,
- setIsLoading,
- openDocEditor,
- isPrivacy,
- isDesktop,
- replaceFileStream,
- t,
- setEncryptionAccess,
- createFolder,
- } = this.props;
- const { itemTitle } = this.state;
-
- setIsLoading(true);
-
- const itemId = e.currentTarget.dataset.itemid;
-
- if (itemTitle.trim() === "") {
- toastr.warning(this.props.t("CreateWithEmptyTitle"));
- return this.completeAction(itemId);
- }
-
- let tab =
- !isDesktop && item.fileExst
- ? window.open(
- combineUrl(
- AppServerConfig.proxyURL,
- config.homepage,
- "/products/files/doceditor"
- ),
- "_blank"
- )
- : null;
-
- !item.fileExst && !item.contentLength
- ? createFolder(item.parentId, itemTitle)
- .then(() => this.completeAction(itemId))
- .then(() =>
- toastr.success(
-
- New folder {{ itemTitle }} is created
-
- )
- )
- .catch((e) => toastr.error(e))
- .finally(() => {
- return setIsLoading(false);
- })
- : createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
- .then((file) => {
- if (isPrivacy) {
- return setEncryptionAccess(file).then((encryptedFile) => {
- if (!encryptedFile) return Promise.resolve();
- toastr.info(t("EncryptedFileSaving"));
- return replaceFileStream(
- file.id,
- encryptedFile,
- true,
- false
- ).then(() =>
- openDocEditor(file.id, file.providerKey, tab, file.webUrl)
- );
- });
- }
- return openDocEditor(file.id, file.providerKey, tab, file.webUrl);
- })
- .then(() => this.completeAction(itemId))
- .then(() => {
- const exst = item.fileExst;
- return toastr.success(
-
- New file {{ itemTitle }}.{{ exst }} is created
-
- );
- })
- .catch((e) => toastr.error(e))
- .finally(() => {
- return setIsLoading(false);
- });
- };
-
- componentDidUpdate(prevProps) {
- const { fileActionId, fileActionExt } = this.props;
- if (fileActionId === -1 && fileActionExt !== prevProps.fileActionExt) {
- const itemTitle = this.getDefaultName(fileActionExt);
- this.setState({ itemTitle });
- }
- // if (fileAction) {
- // if (fileActionId !== prevProps.fileActionId) {
- // this.setState({ editingId: fileActionId });
- // }
- // }
- }
-
- renameTitle = (e) => {
- let title = e.target.value;
- //const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
- const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
- if (title.match(regexp)) {
- toastr.warning(this.props.t("ContainsSpecCharacter"));
- }
- title = title.replace(regexp, "_");
- return this.setState({ itemTitle: title });
- };
-
- cancelUpdateItem = (e) => {
- const originalTitle = getTitleWithoutExst(this.props.item);
- this.setState({
- itemTitle: originalTitle,
- });
-
- return this.completeAction(e);
- };
-
- onClickUpdateItem = (e) => {
- this.props.fileActionType === FileAction.Create
- ? this.createItem(e)
- : this.updateItem(e);
- };
-
- onFilesClick = () => {
- const {
- filter,
- parentFolder,
- setIsLoading,
- fetchFiles,
- isImage,
- isSound,
- isVideo,
- canWebEdit,
- item,
- isTrashFolder,
- openDocEditor,
- expandedKeys,
- addExpandedKeys,
- setMediaViewerData,
- } = this.props;
- const { id, fileExst, viewUrl, providerKey, contentLength } = item;
-
- if (isTrashFolder) return;
-
- if (!fileExst && !contentLength) {
- setIsLoading(true);
-
- if (!expandedKeys.includes(parentFolder + "")) {
- addExpandedKeys(parentFolder + "");
- }
-
- fetchFiles(id, filter)
- .catch((err) => {
- toastr.error(err);
- setIsLoading(false);
- })
- .finally(() => setIsLoading(false));
- } else {
- if (canWebEdit) {
- return openDocEditor(id, providerKey);
- }
-
- if (isImage || isSound || isVideo) {
- setMediaViewerData({ visible: true, id });
- return;
- }
-
- return window.open(viewUrl, "_blank");
- }
- };
-
- onMobileRowClick = () => {
- if (this.props.isTrashFolder || window.innerWidth > 1024) return;
- this.onFilesClick();
- };
-
- getStatusByDate = () => {
- const { culture, t, item, sectionWidth } = this.props;
- const { created, updated, version, fileExst } = item;
-
- const title =
- version > 1
- ? t("TitleModified")
- : fileExst
- ? t("TitleUploaded")
- : t("TitleCreated");
-
- const date = fileExst ? updated : created;
- const dateLabel = new Date(date).toLocaleString(culture);
- const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
-
- return mobile ? dateLabel : `${title}: ${dateLabel}`;
- };
-
- getDefaultName = (format) => {
- const { t } = this.props;
-
- switch (format) {
- case "docx":
- return t("NewDocument");
- case "xlsx":
- return t("NewSpreadsheet");
- case "pptx":
- return t("NewPresentation");
- default:
- return t("NewFolder");
- }
- };
-
- onShowVersionHistory = () => {
- const {
- homepage,
- isTabletView,
- item,
- setIsVerHistoryPanel,
- fetchFileVersions,
- history,
- isTrashFolder,
- } = this.props;
- if (isTrashFolder) return;
-
- if (!isTabletView) {
- fetchFileVersions(item.id + "");
- setIsVerHistoryPanel(true);
- } else {
- history.push(
- combineUrl(AppServerConfig.proxyURL, homepage, `/${item.id}/history`)
- );
- }
- };
-
- onBadgeClick = () => {
- const {
- item,
- selectedFolderPathParts,
- markAsRead,
- setNewFilesPanelVisible,
- setNewFilesIds,
- updateRootBadge,
- updateFileBadge,
- } = this.props;
- if (item.fileExst) {
- markAsRead([], [item.id])
- .then(() => {
- updateRootBadge(selectedFolderPathParts[0], 1);
- updateFileBadge(item.id);
- })
- .catch((err) => toastr.error(err));
- } else {
- setNewFilesPanelVisible(true);
- const newFolderIds = this.props.selectedFolderPathParts;
- newFolderIds.push(item.id);
- setNewFilesIds(newFolderIds);
- }
- };
-
- setConvertDialogVisible = () =>
- this.setState({ showConvertDialog: !this.state.showConvertDialog });
-
- getConvertProgress = (fileId) => {
- const {
- selectedFolderId,
- filter,
- setIsLoading,
- setSecondaryProgressBarData,
- t,
- clearSecondaryProgressData,
- fetchFiles,
- } = this.props;
- getFileConversationProgress(fileId).then((res) => {
- if (res && res[0] && res[0].progress !== 100) {
- setSecondaryProgressBarData({
- icon: "file",
- visible: true,
- percent: res[0].progress,
- label: t("Convert"),
- alert: false,
- });
- setTimeout(() => this.getConvertProgress(fileId), 1000);
- } else {
- if (res[0].error) {
- setSecondaryProgressBarData({
- visible: true,
- alert: true,
- });
- toastr.error(res[0].error);
- setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
- } else {
- setSecondaryProgressBarData({
- icon: "file",
- visible: true,
- percent: 100,
- label: t("Convert"),
- alert: false,
- });
- setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
- const newFilter = filter.clone();
- fetchFiles(selectedFolderId, newFilter)
- .catch((err) => {
- setSecondaryProgressBarData({
- visible: true,
- alert: true,
- });
- //toastr.error(err);
- setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
- })
- .finally(() => setIsLoading(false));
- }
- }
- });
- };
-
- onConvert = () => {
- const { item, t, setSecondaryProgressBarData } = this.props;
- setSecondaryProgressBarData({
- icon: "file",
- visible: true,
- percent: 0,
- label: t("Convert"),
- alert: false,
- });
- this.setState({ showConvertDialog: false }, () =>
- convertFile(item.id).then((convertRes) => {
- if (convertRes && convertRes[0] && convertRes[0].progress !== 100) {
- this.getConvertProgress(item.id);
- }
- })
- );
- };
-
- onClickLock = () => {
- const { item } = this.props;
- const { locked, id } = item;
- this.props.lockFileAction(id, !locked).catch((err) => toastr.error(err));
- };
-
- onClickFavorite = () => {
- const { t, item } = this.props;
- this.props
- .setFavoriteAction("remove", item.id)
- .then(() => toastr.success(t("RemovedFromFavorites")))
- .catch((err) => toastr.error(err));
- };
- render() {
- const {
- t,
- item,
- isTrashFolder,
- isLoading,
- isMobile,
- canWebEdit,
- /* canConvert,*/
- sectionWidth,
- fileActionId,
- fileActionExt,
- } = this.props;
- const { itemTitle, showConvertDialog } = this.state;
- const {
- contentLength,
- updated,
- createdBy,
- fileExst,
- filesCount,
- foldersCount,
- fileStatus,
- id,
- versionGroup,
- locked,
- providerKey,
- } = item;
- const titleWithoutExt = getTitleWithoutExst(item);
- const fileOwner =
- createdBy &&
- ((this.props.viewer.id === createdBy.id && t("AuthorMe")) ||
- createdBy.displayName);
- const updatedDate = updated && this.getStatusByDate();
-
- const accessToEdit =
- item.access === ShareAccessRights.FullAccess ||
- item.access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
- const isEdit = id === fileActionId && fileExst === fileActionExt;
-
- const linkStyles = isTrashFolder //|| window.innerWidth <= 1024
- ? { noHover: true }
- : { onClick: this.onFilesClick };
-
- const newItems = item.new || fileStatus === 2;
- const showNew = !!newItems;
-
- return isEdit ? (
-
- ) : (
- <>
- {showConvertDialog && (
-
- )}
-
-
- {titleWithoutExt}
-
- <>
- {fileExst ? (
-
-
- {fileExst}
-
- {/* TODO: Uncomment after fix conversation {canConvert && !isTrashFolder && (
-
- )} */}
- {canWebEdit && !isTrashFolder && accessToEdit && (
-
- )}
- {locked && (
-
- )}
- {fileStatus === 32 && !isTrashFolder && (
-
- )}
- {fileStatus === 1 && (
-
- )}
- {versionGroup > 1 && (
-
- )}
- {showNew && (
-
- )}
-
- ) : (
-
- {showNew && (
-
- )}
-
- )}
- >
-
- {fileOwner}
-
-
- {(fileExst || contentLength || !providerKey) &&
- updatedDate &&
- updatedDate}
-
-
- {fileExst || contentLength
- ? contentLength
- : !providerKey
- ? `${t("TitleDocuments")}: ${filesCount} | ${t(
- "TitleSubfolders"
- )}: ${foldersCount}`
- : ""}
-
-
- >
- );
- }
-}
-
-export default inject(
- (
- {
- auth,
- filesStore,
- formatsStore,
- uploadDataStore,
- treeFoldersStore,
- selectedFolderStore,
- filesActionsStore,
- mediaViewerDataStore,
- versionHistoryStore,
- dialogsStore,
- },
- { item }
- ) => {
- const { replaceFileStream, setEncryptionAccess } = auth;
- const { culture, isDesktopClient, isTabletView } = auth.settingsStore;
- const { secondaryProgressDataStore } = uploadDataStore;
- const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
- const {
- iconFormatsStore,
- mediaViewersFormatsStore,
- docserviceStore,
- } = formatsStore;
-
- const {
- fetchFiles,
- filter,
- createFile,
- updateFile,
- renameFolder,
- createFolder,
- openDocEditor,
- setIsLoading,
- isLoading,
- updateFileBadge,
- } = filesStore;
-
- const {
- isRecycleBinFolder,
- isPrivacyFolder,
- expandedKeys,
- addExpandedKeys,
- updateRootBadge,
- } = treeFoldersStore;
-
- const {
- type: fileActionType,
- extension: fileActionExt,
- id: fileActionId,
- } = filesStore.fileActionStore;
-
- const {
- setSecondaryProgressBarData,
- clearSecondaryProgressData,
- } = secondaryProgressDataStore;
-
- const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
- const canConvert = docserviceStore.canConvert(item.fileExst);
- const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
- const isImage = iconFormatsStore.isImage(item.fileExst);
- const isSound = iconFormatsStore.isSound(item.fileExst);
-
- const { setMediaViewerData } = mediaViewerDataStore;
- const {
- editCompleteAction,
- lockFileAction,
- setFavoriteAction,
- markAsRead,
- } = filesActionsStore;
-
- const { setNewFilesPanelVisible, setNewFilesIds } = dialogsStore;
-
- return {
- isDesktop: isDesktopClient,
- isTabletView,
- homepage: config.homepage,
- viewer: auth.userStore.user,
- culture,
- fileActionId,
- fileActionType,
- fileActionExt,
- selectedFolderId: selectedFolderStore.id,
- selectedFolderPathParts: selectedFolderStore.pathParts,
- parentFolder: selectedFolderStore.parentId,
- isLoading,
- isTrashFolder: isRecycleBinFolder,
- isPrivacy: isPrivacyFolder,
- filter,
- canWebEdit,
- canConvert,
- isVideo,
- isImage,
- isSound,
- expandedKeys,
-
- setIsLoading,
- fetchFiles,
- setSecondaryProgressBarData,
- clearSecondaryProgressData,
- createFile,
- createFolder,
- updateFile,
- renameFolder,
- replaceFileStream,
- setEncryptionAccess,
- addExpandedKeys,
- openDocEditor,
- editCompleteAction,
- lockFileAction,
- setFavoriteAction,
- setMediaViewerData,
- setIsVerHistoryPanel,
- fetchFileVersions,
- markAsRead,
- setNewFilesPanelVisible,
- setNewFilesIds,
- updateRootBadge,
- updateFileBadge,
- };
- }
-)(withRouter(withTranslation("Home")(observer(FilesRowContent))));
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/SimpleFilesRow.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/SimpleFilesRow.js
deleted file mode 100644
index cadd767582..0000000000
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesRow/SimpleFilesRow.js
+++ /dev/null
@@ -1,721 +0,0 @@
-import React, { useCallback } from "react";
-import { ReactSVG } from "react-svg";
-import styled from "styled-components";
-import { inject, observer } from "mobx-react";
-import { withTranslation } from "react-i18next";
-import IconButton from "@appserver/components/icon-button";
-import Text from "@appserver/components/text";
-import DragAndDrop from "@appserver/components/drag-and-drop";
-import Row from "@appserver/components/row";
-import FilesRowContent from "./FilesRowContent";
-import { withRouter } from "react-router-dom";
-import toastr from "studio/toastr";
-import { FileAction, AppServerConfig } from "@appserver/common/constants";
-import copy from "copy-to-clipboard";
-import config from "../../../../../../../package.json";
-import { combineUrl } from "@appserver/common/utils";
-import { createSelectable } from "react-selectable-fast";
-
-const StyledSimpleFilesRow = styled(Row)`
- margin-top: -2px;
- ${(props) =>
- !props.contextOptions &&
- `
- & > div:last-child {
- width: 0px;
- }
- `}
-
- .share-button-icon {
- margin-right: 7px;
- margin-top: -1px;
- }
-
- .share-button:hover,
- .share-button-icon:hover {
- cursor: pointer;
- color: #657077;
- path {
- fill: #657077;
- }
- }
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-
- @media (max-width: 1312px) {
- .share-button {
- padding-top: 3px;
- }
- }
-
- .styled-element {
- margin-right: 7px;
- }
-`;
-
-const EncryptedFileIcon = styled.div`
- background: url("images/security.svg") no-repeat 0 0 / 16px 16px transparent;
- height: 16px;
- position: absolute;
- width: 16px;
- margin-top: 14px;
- margin-left: ${(props) => (props.isEdit ? "40px" : "12px")};
-`;
-
-const svgLoader = () => ;
-
-const SimpleFilesRow = createSelectable((props) => {
- const {
- t,
- item,
- sectionWidth,
- actionType,
- actionExtension,
- isPrivacy,
- isRecycleBin,
- dragging,
- checked,
- canShare,
- isFolder,
- draggable,
- isRootFolder,
- homepage,
- isTabletView,
- actionId,
- selectedFolderId,
-
- setSharingPanelVisible,
- setChangeOwnerPanelVisible,
- setDeleteThirdPartyDialogVisible,
- setRemoveItem,
- setMoveToPanelVisible,
- setCopyPanelVisible,
- openDocEditor,
- setIsVerHistoryPanel,
- fetchFileVersions,
- setAction,
- deleteFileAction,
- deleteFolderAction,
- lockFileAction,
- duplicateAction,
- finalizeVersionAction,
- setFavoriteAction,
- openLocationAction,
- selectRowAction,
- setThirdpartyInfo,
- setMediaViewerData,
- setDragging,
- setStartDrag,
- startUpload,
- onSelectItem,
- history,
- setTooltipPosition,
- setDownloadDialogVisible,
- downloadAction,
- confirmDelete,
- setDeleteDialogVisible,
- } = props;
-
- const {
- id,
- title,
- fileExst,
- contentLength,
- shared,
- access,
- contextOptions,
- icon,
- providerKey,
- folderId,
- viewUrl,
- webUrl,
- canOpenPlayer,
- locked,
- parentId,
- } = item;
-
- const isThirdPartyFolder = providerKey && isRootFolder;
-
- const onContentRowSelect = (checked, file) => {
- if (!file) return;
-
- selectRowAction(checked, file);
- };
-
- const onClickShare = () => {
- onSelectItem(item);
- setSharingPanelVisible(true);
- };
- const onOwnerChange = () => setChangeOwnerPanelVisible(true);
- const onMoveAction = () => setMoveToPanelVisible(true);
- const onCopyAction = () => setCopyPanelVisible(true);
-
- const getSharedButton = (shared) => {
- const color = shared ? "#657077" : "#a3a9ae";
- return (
-
-
- {t("Share")}
-
- );
- };
-
- const getItemIcon = (isEdit) => {
- return (
- <>
-
- {isPrivacy && fileExst && }
- >
- );
- };
-
- const onOpenLocation = () => {
- const locationId = isFolder ? id : folderId;
- openLocationAction(locationId, isFolder);
- };
-
- const showVersionHistory = () => {
- if (!isTabletView) {
- fetchFileVersions(id + "");
- setIsVerHistoryPanel(true);
- } else {
- history.push(
- combineUrl(AppServerConfig.proxyURL, homepage, `/${id}/history`)
- );
- }
- };
-
- const finalizeVersion = () =>
- finalizeVersionAction(id).catch((err) => toastr.error(err));
-
- const onClickFavorite = (e) => {
- const data = (e.currentTarget && e.currentTarget.dataset) || e;
- const { action } = data;
-
- setFavoriteAction(action, id)
- .then(() =>
- action === "mark"
- ? toastr.success(t("MarkedAsFavorite"))
- : toastr.success(t("RemovedFromFavorites"))
- )
- .catch((err) => toastr.error(err));
- };
-
- const lockFile = () =>
- lockFileAction(id, !locked).catch((err) => toastr.error(err));
-
- const onClickLinkForPortal = () => {
- const isFile = !!fileExst;
- copy(
- isFile
- ? canOpenPlayer
- ? `${window.location.href}&preview=${id}`
- : webUrl
- : `${window.location.origin + homepage}/filter?folder=${id}`
- );
-
- toastr.success(t("LinkCopySuccess"));
- };
-
- const onClickLinkEdit = () => openDocEditor(id, providerKey);
-
- const onClickDownload = () => {
- const isFile = !!fileExst && contentLength;
- isFile
- ? window.open(viewUrl, "_blank")
- : downloadAction(t("ArchivingData")).catch((err) => toastr.error(err));
- };
-
- const onClickDownloadAs = () => setDownloadDialogVisible(true);
-
- const onDuplicate = () =>
- duplicateAction(item, t("CopyOperation")).catch((err) => toastr.error(err));
-
- const onClickRename = () => {
- setAction({
- type: FileAction.Rename,
- extension: fileExst,
- id,
- });
- };
-
- const onChangeThirdPartyInfo = () => setThirdpartyInfo(providerKey);
-
- const onMediaFileClick = (fileId) => {
- const itemId = typeof fileId !== "object" ? fileId : id;
- setMediaViewerData({ visible: true, id: itemId });
- };
-
- const onClickDelete = () => {
- if (isThirdPartyFolder) {
- const splitItem = id.split("-");
- setRemoveItem({ id: splitItem[splitItem.length - 1], title });
- setDeleteThirdPartyDialogVisible(true);
- return;
- }
-
- if (confirmDelete) {
- setDeleteDialogVisible(true);
- } else {
- const translations = {
- deleteOperation: t("DeleteOperation"),
- };
-
- fileExst || contentLength
- ? deleteFileAction(id, folderId, translations)
- .then(() => toastr.success(t("FileRemoved")))
- .catch((err) => toastr.error(err))
- : deleteFolderAction(id, parentId, translations)
- .then(() => toastr.success(t("FolderRemoved")))
- .catch((err) => toastr.error(err));
- }
- };
-
- const rowContextClick = () => {
- onSelectItem(item);
- };
-
- const getFilesContextOptions = useCallback(() => {
- const isSharable = access !== 1 && access !== 0;
- return contextOptions.map((option) => {
- switch (option) {
- case "open":
- return {
- key: option,
- label: t("Open"),
- icon: "images/catalog.folder.react.svg",
- onClick: onOpenLocation,
- disabled: false,
- };
- case "show-version-history":
- return {
- key: option,
- label: t("ShowVersionHistory"),
- icon: "images/history.react.svg",
- onClick: showVersionHistory,
- disabled: false,
- };
- case "finalize-version":
- return {
- key: option,
- label: t("FinalizeVersion"),
- icon: "images/history-finalized.react.svg",
- onClick: finalizeVersion,
- disabled: false,
- };
- case "separator0":
- case "separator1":
- case "separator2":
- case "separator3":
- return { key: option, isSeparator: true };
- case "open-location":
- return {
- key: option,
- label: t("OpenLocation"),
- icon: "images/download-as.react.svg",
- onClick: onOpenLocation,
- disabled: false,
- };
- case "mark-as-favorite":
- return {
- key: option,
- label: t("MarkAsFavorite"),
- icon: "images/favorites.react.svg",
- onClick: onClickFavorite,
- disabled: false,
- "data-action": "mark",
- action: "mark",
- };
- case "block-unblock-version":
- return {
- key: option,
- label: t("UnblockVersion"),
- icon: "images/lock.react.svg",
- onClick: lockFile,
- disabled: false,
- };
- case "sharing-settings":
- return {
- key: option,
- label: t("SharingSettings"),
- icon: "images/catalog.shared.react.svg",
- onClick: onClickShare,
- disabled: isSharable,
- };
- case "send-by-email":
- return {
- key: option,
- label: t("SendByEmail"),
- icon: "/static/images/mail.react.svg",
- disabled: true,
- };
- case "owner-change":
- return {
- key: option,
- label: t("ChangeOwner"),
- icon: "images/catalog.user.react.svg",
- onClick: onOwnerChange,
- disabled: false,
- };
- case "link-for-portal-users":
- return {
- key: option,
- label: t("LinkForPortalUsers"),
- icon: "/static/images/invitation.link.react.svg",
- onClick: onClickLinkForPortal,
- disabled: false,
- };
- case "edit":
- return {
- key: option,
- label: t("Edit"),
- icon: "/static/images/access.edit.react.svg",
- onClick: onClickLinkEdit,
- disabled: false,
- };
- case "preview":
- return {
- key: option,
- label: t("Preview"),
- icon: "EyeIcon",
- onClick: onClickLinkEdit,
- disabled: true,
- };
- case "view":
- return {
- key: option,
- label: t("View"),
- icon: "/static/images/eye.react.svg",
- onClick: onMediaFileClick,
- disabled: false,
- };
- case "download":
- return {
- key: option,
- label: t("Download"),
- icon: "images/download.react.svg",
- onClick: onClickDownload,
- disabled: false,
- };
- case "download-as":
- return {
- key: option,
- label: t("DownloadAs"),
- icon: "images/download-as.react.svg",
- onClick: onClickDownloadAs,
- disabled: false,
- };
- case "move-to":
- return {
- key: option,
- label: t("MoveTo"),
- icon: "images/move.react.svg",
- onClick: onMoveAction,
- disabled: false,
- };
- case "restore":
- return {
- key: option,
- label: t("Restore"),
- icon: "images/move.react.svg",
- onClick: onMoveAction,
- disabled: false,
- };
- case "copy-to":
- return {
- key: option,
- label: t("Copy"),
- icon: "/static/images/copy.react.svg",
- onClick: onCopyAction,
- disabled: false,
- };
- case "copy":
- return {
- key: option,
- label: t("Duplicate"),
- icon: "/static/images/copy.react.svg",
- onClick: onDuplicate,
- disabled: false,
- };
- case "rename":
- return {
- key: option,
- label: t("Rename"),
- icon: "images/rename.react.svg",
- onClick: onClickRename,
- disabled: false,
- };
- case "change-thirdparty-info":
- return {
- key: option,
- label: t("ThirdPartyInfo"),
- icon: "/static/images/access.edit.react.svg",
- onClick: onChangeThirdPartyInfo,
- disabled: false,
- };
- case "delete":
- return {
- key: option,
- label: isThirdPartyFolder ? t("DeleteThirdParty") : t("Delete"),
- icon: "/static/images/catalog.trash.react.svg",
- onClick: onClickDelete,
- disabled: false,
- };
- case "remove-from-favorites":
- return {
- key: option,
- label: t("RemoveFromFavorites"),
- icon: "images/favorites.react.svg",
- onClick: onClickFavorite,
- disabled: false,
- "data-action": "remove",
- action: "remove",
- };
- default:
- break;
- }
-
- return undefined;
- });
- }, [contextOptions, item]);
-
- const onDropZoneUpload = (files, uploadToFolder) => {
- const folderId = uploadToFolder ? uploadToFolder : selectedFolderId;
-
- dragging && setDragging(false);
- startUpload(files, folderId, t);
- };
-
- const onDrop = (items) => {
- if (!fileExst) {
- onDropZoneUpload(items, id);
- } else {
- onDropZoneUpload(items, selectedFolderId);
- }
- };
-
- const onMouseDown = (e) => {
- if (!draggable) {
- return;
- }
-
- if (
- window.innerWidth < 1025 ||
- e.target.tagName === "rect" ||
- e.target.tagName === "path"
- ) {
- return;
- }
- const mouseButton = e.which
- ? e.which !== 1
- : e.button
- ? e.button !== 0
- : false;
- const label = e.currentTarget.getAttribute("label");
- if (mouseButton || e.currentTarget.tagName !== "DIV" || label) {
- return;
- }
-
- setTooltipPosition(e.pageX, e.pageY);
- setStartDrag(true);
- };
-
- const isMobile = sectionWidth < 500;
- const isEdit =
- !!actionType && actionId === id && fileExst === actionExtension;
- const contextOptionsProps =
- !isEdit && contextOptions && contextOptions.length > 0
- ? {
- contextOptions: getFilesContextOptions(),
- }
- : {};
-
- const isDragging = isFolder && access < 2 && !isRecycleBin;
- const checkedProps = isEdit || id <= 0 ? {} : { checked };
- const element = getItemIcon(isEdit || id <= 0);
- const displayShareButton = isMobile ? "26px" : !canShare ? "38px" : "96px";
- let className = isDragging ? " droppable" : "";
- if (draggable) className += " draggable not-selectable";
-
- let value = fileExst || contentLength ? `file_${id}` : `folder_${id}`;
- value += draggable ? "_draggable" : "";
-
- const sharedButton =
- !canShare || (isPrivacy && !fileExst) || isEdit || id <= 0 || isMobile
- ? null
- : getSharedButton(shared);
-
- return (
-
-
-
-
-
-
-
- );
-});
-
-export default inject(
- (
- {
- auth,
- filesStore,
- treeFoldersStore,
- selectedFolderStore,
- dialogsStore,
- versionHistoryStore,
- filesActionsStore,
- mediaViewerDataStore,
- uploadDataStore,
- settingsStore,
- },
- { item }
- ) => {
- const { isTabletView } = auth.settingsStore;
- const { type, extension, id } = filesStore.fileActionStore;
- const { isRecycleBinFolder, isPrivacyFolder } = treeFoldersStore;
-
- const {
- setSharingPanelVisible,
- setChangeOwnerPanelVisible,
- setRemoveItem,
- setDeleteThirdPartyDialogVisible,
- setMoveToPanelVisible,
- setCopyPanelVisible,
- setDownloadDialogVisible,
- setDeleteDialogVisible,
- } = dialogsStore;
-
- const {
- selection,
- canShare,
- openDocEditor,
- fileActionStore,
- dragging,
- setDragging,
- setStartDrag,
- setTooltipPosition,
- isFileSelected,
- } = filesStore;
-
- const { isRootFolder, id: selectedFolderId } = selectedFolderStore;
- const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
- const { setAction } = fileActionStore;
-
- const selectedItem = selection.find(
- (x) => x.id === item.id && x.fileExst === item.fileExst
- );
-
- const isFolder = selectedItem
- ? false
- : item.fileExst || item.contentLength
- ? false
- : true;
-
- const draggable =
- !isRecycleBinFolder && selectedItem && selectedItem.id !== id;
-
- const {
- deleteFileAction,
- deleteFolderAction,
- lockFileAction,
- finalizeVersionAction,
- duplicateAction,
- setFavoriteAction,
- openLocationAction,
- selectRowAction,
- setThirdpartyInfo,
- onSelectItem,
- downloadAction,
- } = filesActionsStore;
-
- const { setMediaViewerData } = mediaViewerDataStore;
- const { startUpload } = uploadDataStore;
-
- return {
- dragging,
- actionType: type,
- actionExtension: extension,
- isPrivacy: isPrivacyFolder,
- isRecycleBin: isRecycleBinFolder,
- isRootFolder,
- canShare,
- checked: isFileSelected(item.id, item.parentId),
- isFolder,
- draggable,
- isItemsSelected: !!selection.length,
- homepage: config.homepage,
- isTabletView,
- actionId: fileActionStore.id,
- setSharingPanelVisible,
- setChangeOwnerPanelVisible,
- setRemoveItem,
- setDeleteThirdPartyDialogVisible,
- setMoveToPanelVisible,
- setCopyPanelVisible,
- setDownloadDialogVisible,
- openDocEditor,
- setIsVerHistoryPanel,
- fetchFileVersions,
- setAction,
- deleteFileAction,
- deleteFolderAction,
- lockFileAction,
- finalizeVersionAction,
- duplicateAction,
- setFavoriteAction,
- openLocationAction,
- selectRowAction,
- setThirdpartyInfo,
- setMediaViewerData,
- selectedFolderId,
- setDragging,
- setStartDrag,
- startUpload,
- onSelectItem,
- setTooltipPosition,
- downloadAction,
- confirmDelete: settingsStore.confirmDelete,
- setDeleteDialogVisible,
- };
- }
-)(withTranslation("Home")(observer(withRouter(SimpleFilesRow))));
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTileContent.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTileContent.js
deleted file mode 100644
index bde292e9ab..0000000000
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTileContent.js
+++ /dev/null
@@ -1,500 +0,0 @@
-import React from "react";
-import { withRouter } from "react-router";
-import { Trans, withTranslation } from "react-i18next";
-import styled from "styled-components";
-import Badge from "@appserver/components/badge";
-import Link from "@appserver/components/link";
-import Text from "@appserver/components/text";
-import { markAsRead } from "@appserver/common/api/files";
-import { FileAction, AppServerConfig } from "@appserver/common/constants";
-import toastr from "studio/toastr";
-import { getTitleWithoutExst } from "../../../../../helpers/files-helpers";
-import { NewFilesPanel } from "../../../../panels";
-import EditingWrapperComponent from "./EditingWrapperComponent";
-import TileContent from "./TileContent";
-import { isMobile } from "react-device-detect";
-import { inject, observer } from "mobx-react";
-import CheckIcon from "../../../../../../public/images/check.react.svg";
-import CrossIcon from "../../../../../../../../../public/images/cross.react.svg";
-import config from "../../../../../../package.json";
-import { combineUrl } from "@appserver/common/utils";
-
-const SimpleFilesTileContent = styled(TileContent)`
- .rowMainContainer {
- height: auto;
- max-width: 100%;
- align-self: flex-end;
-
- a {
- word-break: break-word;
- }
- }
-
- .mainIcons {
- align-self: flex-end;
- }
-
- .badge-ext {
- margin-left: -8px;
- margin-right: 8px;
- }
-
- .badge {
- margin-right: 8px;
- }
-
- .badges {
- display: flex;
- align-items: center;
- }
-
- .share-icon {
- margin-top: -4px;
- padding-right: 8px;
- }
-
- @media (max-width: 1024px) {
- display: inline-flex;
- height: auto;
-
- & > div {
- margin-top: 0;
- }
- }
-`;
-
-const okIcon = (
-
-);
-
-const cancelIcon = (
-
-);
-
-class FilesTileContent extends React.PureComponent {
- constructor(props) {
- super(props);
- let titleWithoutExt = getTitleWithoutExst(props.item);
-
- if (props.fileAction.id === -1) {
- titleWithoutExt = this.getDefaultName(props.fileAction.extension);
- }
-
- this.state = {
- itemTitle: titleWithoutExt,
- editingId: props.fileAction.id,
- showNewFilesPanel: false,
- newFolderId: [],
- newItems: props.item.new,
- //loading: false
- };
- }
-
- completeAction = (e) => {
- //this.setState({ loading: false }, () =>)
- this.props.onEditComplete(e);
- };
-
- updateItem = (e) => {
- const {
- fileAction,
- updateFile,
- renameFolder,
- item,
- setIsLoading,
- } = this.props;
-
- const { itemTitle } = this.state;
- const originalTitle = getTitleWithoutExst(item);
-
- setIsLoading(true);
- if (originalTitle === itemTitle) return this.completeAction(e);
-
- item.fileExst
- ? updateFile(fileAction.id, itemTitle)
- .then(() => this.completeAction(e))
- .finally(() => setIsLoading(false))
- : renameFolder(fileAction.id, itemTitle)
- .then(() => this.completeAction(e))
- .finally(() => setIsLoading(false));
- };
-
- createItem = (e) => {
- const { createFile, item, setIsLoading, createFolder, t } = this.props;
- const { itemTitle } = this.state;
-
- setIsLoading(true);
-
- if (itemTitle.trim() === "") return this.completeAction();
-
- !item.fileExst
- ? createFolder(item.parentId, itemTitle)
- .then(() => this.completeAction(e))
- .finally(() => {
- toastr.success(
-
- New folder {{ itemTitle }} is created
-
- );
- return setIsLoading(false);
- })
- : createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
- .then(() => this.completeAction(e))
- .finally(() => {
- const exst = item.fileExst;
- toastr.success(
-
- New file {{ itemTitle }}.{{ exst }} is created
-
- );
- return setIsLoading(false);
- });
- };
-
- componentDidUpdate(prevProps) {
- const { fileAction } = this.props;
- if (fileAction) {
- if (fileAction.id !== prevProps.fileAction.id) {
- this.setState({ editingId: fileAction.id });
- }
- }
- }
-
- renameTitle = (e) => {
- this.setState({ itemTitle: e.target.value });
- };
-
- cancelUpdateItem = (e) => {
- //this.setState({ loading: false });
- this.completeAction(e);
- };
-
- onClickUpdateItem = () => {
- this.props.fileAction.type === FileAction.Create
- ? this.createItem()
- : this.updateItem();
- };
-
- onKeyUpUpdateItem = (e) => {
- if (e.keyCode === 13) {
- this.props.fileAction.type === FileAction.Create
- ? this.createItem()
- : this.updateItem();
- }
-
- if (e.keyCode === 27) return this.cancelUpdateItem();
- };
-
- onFilesClick = () => {
- const { id, fileExst, viewUrl, providerKey } = this.props.item;
- const {
- filter,
- parentFolder,
- setIsLoading,
- onMediaFileClick,
- fetchFiles,
- canWebEdit,
- openDocEditor,
- isVideo,
- isImage,
- isSound,
- expandedKeys,
- addExpandedKeys,
- } = this.props;
- if (!fileExst) {
- setIsLoading(true);
-
- if (!expandedKeys.includes(parentFolder + "")) {
- addExpandedKeys(parentFolder + "");
- }
-
- fetchFiles(id, filter)
- .catch((err) => {
- toastr.error(err);
- setIsLoading(false);
- })
- .finally(() => setIsLoading(false));
- } else {
- if (canWebEdit) {
- return openDocEditor(id, providerKey);
- }
-
- const isOpenMedia = isImage || isSound || isVideo;
-
- if (isOpenMedia) {
- onMediaFileClick(id);
- return;
- }
-
- return window.open(viewUrl, "_blank");
- }
- };
-
- onMobileRowClick = (e) => {
- if (!isMobile) return;
-
- this.onFilesClick();
- };
-
- getStatusByDate = () => {
- const { culture, t, item, sectionWidth } = this.props;
- const { created, updated, version, fileExst } = item;
-
- const title =
- version > 1
- ? t("TitleModified")
- : fileExst
- ? t("TitleUploaded")
- : t("TitleCreated");
-
- const date = fileExst ? updated : created;
- const dateLabel = new Date(date).toLocaleString(culture);
- const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
-
- return mobile ? dateLabel : `${title}: ${dateLabel}`;
- };
-
- getDefaultName = (format) => {
- const { t } = this.props;
-
- switch (format) {
- case "docx":
- return t("NewDocument");
- case "xlsx":
- return t("NewSpreadsheet");
- case "pptx":
- return t("NewPresentation");
- default:
- return t("NewFolder");
- }
- };
-
- onShowVersionHistory = (e) => {
- const { homepage, history } = this.props;
- const fileId = e.currentTarget.dataset.id;
-
- history.push(
- combineUrl(AppServerConfig.proxyURL, homepage, `/${fileId}/history`)
- );
- };
-
- onBadgeClick = () => {
- const { showNewFilesPanel } = this.state;
- const {
- item,
- treeFolders,
- setTreeFolders,
- rootFolderId,
- newItems,
- filter,
- fetchFiles,
- } = this.props;
- if (item.fileExst) {
- markAsRead([], [item.id])
- .then(() => {
- const data = treeFolders;
- const dataItem = data.find((x) => x.id === rootFolderId);
- dataItem.newItems = newItems ? dataItem.newItems - 1 : 0; //////newItems
- setTreeFolders(data);
- fetchFiles(this.props.selectedFolderId, filter.clone());
- })
- .catch((err) => toastr.error(err));
- } else {
- const newFolderId = this.props.selectedFolderPathParts;
- newFolderId.push(item.id);
- this.setState({
- showNewFilesPanel: !showNewFilesPanel,
- newFolderId,
- });
- }
- };
-
- onShowNewFilesPanel = () => {
- const { showNewFilesPanel } = this.state;
- this.setState({ showNewFilesPanel: !showNewFilesPanel });
- };
-
- render() {
- const { item, fileAction, isTrashFolder, folders } = this.props;
- const {
- itemTitle,
- editingId,
- showNewFilesPanel,
- newItems,
- newFolderId,
- } = this.state;
- const { fileExst, id } = item;
-
- const titleWithoutExt = getTitleWithoutExst(item);
-
- const isEdit = id === editingId && fileExst === fileAction.extension;
- const linkStyles = isTrashFolder
- ? { noHover: true }
- : { onClick: this.onFilesClick };
- const showNew = item.new && item.new > 0;
-
- return isEdit ? (
-
- ) : (
- <>
- {showNewFilesPanel && (
-
- )}
-
-
- {titleWithoutExt}
-
- <>
- {fileExst ? (
-
-
- {fileExst}
-
-
- ) : (
-
- {!!showNew && (
-
- )}
-
- )}
- >
-
- >
- );
- }
-}
-
-export default inject(
- (
- { auth, filesStore, formatsStore, treeFoldersStore, selectedFolderStore },
- { item }
- ) => {
- const { culture } = auth.settingsStore;
- const {
- iconFormatsStore,
- mediaViewersFormatsStore,
- docserviceStore,
- } = formatsStore;
- const {
- folders,
- fetchFiles,
- filter,
- newRowItems,
- createFile,
- updateFile,
- renameFolder,
- createFolder,
- setIsLoading,
- isLoading,
- dragging,
- } = filesStore;
-
- const {
- treeFolders,
- setTreeFolders,
- isRecycleBinFolder,
- expandedKeys,
- addExpandedKeys,
- } = treeFoldersStore;
-
- const { type, extension, id } = filesStore.fileActionStore;
-
- const fileAction = { type, extension, id };
-
- const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
- const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
- const isImage = iconFormatsStore.isImage(item.fileExst);
- const isSound = iconFormatsStore.isSound(item.fileExst);
-
- return {
- culture,
- homepage: config.homepage,
- fileAction,
- folders,
- rootFolderId: selectedFolderStore.pathParts,
- selectedFolderId: selectedFolderStore.id,
- selectedFolderPathParts: selectedFolderStore.pathParts,
- newItems: selectedFolderStore.new,
- parentFolder: selectedFolderStore.parentId,
- isLoading,
- treeFolders,
- isTrashFolder: isRecycleBinFolder,
- filter,
- dragging,
- canWebEdit,
- isVideo,
- isImage,
- isSound,
- newRowItems,
- expandedKeys,
-
- setIsLoading,
- fetchFiles,
- setTreeFolders,
- createFile,
- createFolder,
- updateFile,
- renameFolder,
- addExpandedKeys,
- };
- }
-)(withRouter(withTranslation("Home")(observer(FilesTileContent))));
diff --git a/products/ASC.Files/Client/src/components/panels/VersionHistoryPanel/index.js b/products/ASC.Files/Client/src/components/panels/VersionHistoryPanel/index.js
index 01355c8f2a..250dd4d13f 100644
--- a/products/ASC.Files/Client/src/components/panels/VersionHistoryPanel/index.js
+++ b/products/ASC.Files/Client/src/components/panels/VersionHistoryPanel/index.js
@@ -12,7 +12,7 @@ import {
StyledHeaderContent,
StyledBody,
} from "../StyledPanels";
-import { SectionBodyContent } from "../../pages/VersionHistory/Section/";
+import { SectionBodyContent } from "../../../pages/VersionHistory/Section/";
import { inject, observer } from "mobx-react";
import config from "../../../../package.json";
diff --git a/products/ASC.Files/Client/src/components/pages/Home/MediaViewer/index.js b/products/ASC.Files/Client/src/pages/Home/MediaViewer/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/MediaViewer/index.js
rename to products/ASC.Files/Client/src/pages/Home/MediaViewer/index.js
diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContainer.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContainer.js
new file mode 100644
index 0000000000..96518e92ef
--- /dev/null
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContainer.js
@@ -0,0 +1,35 @@
+import React from "react";
+import { inject, observer } from "mobx-react";
+import RowContainer from "@appserver/components/row-container";
+import { Consumer } from "@appserver/components/utils/context";
+import SimpleFilesRow from "./SimpleFilesRow";
+
+const FilesRowContainer = ({ filesList }) => {
+ return (
+
+ {(context) => (
+
+ {filesList.map((item, index) => (
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default inject(({ filesStore }) => {
+ const { filesList } = filesStore;
+
+ return {
+ filesList,
+ };
+})(observer(FilesRowContainer));
diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContent.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContent.js
new file mode 100644
index 0000000000..84e2782144
--- /dev/null
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/FilesRowContent.js
@@ -0,0 +1,164 @@
+import React from "react";
+import { withRouter } from "react-router";
+import { withTranslation } from "react-i18next";
+import styled from "styled-components";
+import { isMobile } from "react-device-detect";
+
+import Link from "@appserver/components/link";
+import Text from "@appserver/components/text";
+import RowContent from "@appserver/components/row-content";
+
+import withContent from "../../../../../HOCs/withContent";
+import withBadges from "../../../../../HOCs/withBadges";
+
+const sideColor = "#A3A9AE";
+
+const SimpleFilesRowContent = styled(RowContent)`
+ .badge-ext {
+ margin-left: -8px;
+ margin-right: 8px;
+ }
+
+ .badge {
+ height: 14px;
+ width: 14px;
+ margin-right: 6px;
+ }
+ .lock-file {
+ cursor: pointer;
+ }
+ .badges {
+ display: flex;
+ align-items: center;
+ }
+
+ .favorite {
+ cursor: pointer;
+ margin-right: 6px;
+ }
+
+ .share-icon {
+ margin-top: -4px;
+ padding-right: 8px;
+ }
+
+ .row_update-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+`;
+
+const FilesRowContent = ({
+ t,
+ item,
+ sectionWidth,
+ titleWithoutExt,
+ updatedDate,
+ fileOwner,
+ linkStyles,
+ isTrashFolder,
+ onFilesClick,
+ badgesComponent,
+}) => {
+ const {
+ contentLength,
+ fileExst,
+ filesCount,
+ foldersCount,
+ providerKey,
+ } = item;
+
+ const onMobileRowClick = () => {
+ if (isTrashFolder || window.innerWidth > 1024) return;
+ onFilesClick();
+ };
+
+ return (
+ <>
+
+
+ {titleWithoutExt}
+
+
+
+ {fileExst ? (
+
+ {fileExst}
+
+ ) : null}
+ {badgesComponent}
+
+
+ {fileOwner}
+
+
+ {(fileExst || contentLength || !providerKey) &&
+ updatedDate &&
+ updatedDate}
+
+
+ {fileExst || contentLength
+ ? contentLength
+ : !providerKey
+ ? `${t("TitleDocuments")}: ${filesCount} | ${t(
+ "TitleSubfolders"
+ )}: ${foldersCount}`
+ : ""}
+
+
+ >
+ );
+};
+
+export default withRouter(
+ withTranslation("Home")(withContent(withBadges(FilesRowContent)))
+);
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
new file mode 100644
index 0000000000..7c9fbcbb3d
--- /dev/null
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/RowsView/SimpleFilesRow.js
@@ -0,0 +1,107 @@
+import React from "react";
+import styled from "styled-components";
+import { withTranslation } from "react-i18next";
+import DragAndDrop from "@appserver/components/drag-and-drop";
+import Row from "@appserver/components/row";
+import FilesRowContent from "./FilesRowContent";
+import { withRouter } from "react-router-dom";
+import { createSelectable } from "react-selectable-fast";
+
+import withFileActions from "../../../../../HOCs/withFileActions";
+import withContextOptions from "../../../../../HOCs/withContextOptions";
+
+const StyledSimpleFilesRow = styled(Row)`
+ margin-top: -2px;
+ ${(props) =>
+ !props.contextOptions &&
+ `
+ & > div:last-child {
+ width: 0px;
+ }
+ `}
+
+ .share-button-icon {
+ margin-right: 7px;
+ margin-top: -1px;
+ }
+
+ .share-button:hover,
+ .share-button-icon:hover {
+ cursor: pointer;
+ color: #657077;
+ path {
+ fill: #657077;
+ }
+ }
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+
+ @media (max-width: 1312px) {
+ .share-button {
+ padding-top: 3px;
+ }
+ }
+
+ .styled-element {
+ margin-right: 7px;
+ }
+`;
+
+const SimpleFilesRow = createSelectable((props) => {
+ const {
+ item,
+ sectionWidth,
+ dragging,
+ onContentRowSelect,
+ rowContextClick,
+ onDrop,
+ onMouseDown,
+ className,
+ isDragging,
+ value,
+ displayShareButton,
+ isPrivacy,
+ sharedButton,
+ contextOptionsProps,
+ checkedProps,
+ element,
+ onFilesClick,
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+ );
+});
+
+export default withTranslation("Home")(
+ withFileActions(withContextOptions(withRouter(SimpleFilesRow)))
+);
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/BadgesFileTile.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/BadgesFileTile.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/BadgesFileTile.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/BadgesFileTile.js
diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FileTile.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FileTile.js
new file mode 100644
index 0000000000..868c7e560d
--- /dev/null
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FileTile.js
@@ -0,0 +1,64 @@
+import React from "react";
+import styled from "styled-components";
+import { withTranslation } from "react-i18next";
+import DragAndDrop from "@appserver/components/drag-and-drop";
+
+import Tile from "./sub-components/Tile";
+import FilesTileContent from "./FilesTileContent";
+import { withRouter } from "react-router-dom";
+import { createSelectable } from "react-selectable-fast";
+
+import withFileActions from "../hoc/withFileActions";
+
+const FilesTile = createSelectable((props) => {
+ const {
+ item,
+ sectionWidth,
+ dragging,
+ onContentRowSelect,
+ rowContextClick,
+ onDrop,
+ onMouseDown,
+ className,
+ isDragging,
+ value,
+ displayShareButton,
+ isPrivacy,
+ sharedButton,
+ contextOptionsProps,
+ checkedProps,
+ element,
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+ );
+});
+
+export default withTranslation("Home")(withFileActions(withRouter(FilesTile)));
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTile/FilesTileContainer.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FilesTileContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/FilesTile/FilesTileContainer.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FilesTileContainer.js
diff --git a/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FilesTileContent.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FilesTileContent.js
new file mode 100644
index 0000000000..0fc01424cc
--- /dev/null
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/FilesTileContent.js
@@ -0,0 +1,153 @@
+import React from "react";
+import { withRouter } from "react-router";
+import { withTranslation } from "react-i18next";
+import styled from "styled-components";
+
+import Link from "@appserver/components/link";
+import Text from "@appserver/components/text";
+
+import TileContent from "./TileContent";
+import withContent from "../hoc/withContent";
+import Badges from "../sub-components/Badges";
+
+const SimpleFilesTileContent = styled(TileContent)`
+ .rowMainContainer {
+ height: auto;
+ max-width: 100%;
+ align-self: flex-end;
+
+ a {
+ word-break: break-word;
+ }
+ }
+
+ .mainIcons {
+ align-self: flex-end;
+ }
+
+ .badge-ext {
+ margin-left: -8px;
+ margin-right: 8px;
+ }
+
+ .badge {
+ margin-right: 8px;
+ }
+
+ .badges {
+ display: flex;
+ align-items: center;
+ }
+
+ .share-icon {
+ margin-top: -4px;
+ padding-right: 8px;
+ }
+
+ @media (max-width: 1024px) {
+ display: inline-flex;
+ height: auto;
+
+ & > div {
+ margin-top: 0;
+ }
+ }
+`;
+
+const FilesTileContent = ({
+ t,
+ item,
+ sectionWidth,
+ titleWithoutExt,
+ updatedDate,
+ fileOwner,
+ accessToEdit,
+ linkStyles,
+ newItems,
+ showNew,
+ canWebEdit,
+ canConvert,
+ isTrashFolder,
+ onFilesClick,
+ onShowVersionHistory,
+ onBadgeClick,
+ onClickLock,
+ onClickFavorite,
+ setConvertDialogVisible,
+}) => {
+ const {
+ contentLength,
+ fileExst,
+ filesCount,
+ foldersCount,
+ fileStatus,
+ id,
+ versionGroup,
+ locked,
+ providerKey,
+ } = item;
+
+ const onMobileRowClick = () => {
+ if (isTrashFolder || window.innerWidth > 1024) return;
+ onFilesClick();
+ };
+
+ return (
+ <>
+
+
+ {titleWithoutExt}
+ {fileExst ? (
+
+ {fileExst}
+
+ ) : null}
+
+
+
+
+
+
+ >
+ );
+};
+
+export default withRouter(
+ withTranslation("Home")(withContent(FilesTileContent))
+);
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/Tile.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/Tile.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/Tile.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/Tile.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/TileContainer.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/TileContainer.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/TileContainer.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/TileContainer.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/TileContent.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/TileContent.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/TileContent.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/TilesView/sub-components/TileContent.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Body/index.js
similarity index 81%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Body/index.js
index 640d9d3f4a..282eeafba5 100644
--- a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js
+++ b/products/ASC.Files/Client/src/pages/Home/Section/Body/index.js
@@ -1,25 +1,22 @@
-import React, { useEffect, useState } from "react";
+import React, { useEffect } from "react";
import { withRouter } from "react-router";
import { withTranslation } from "react-i18next";
-import Loaders from "@appserver/common/components/Loaders";
import { isMobile } from "react-device-detect";
import { observer, inject } from "mobx-react";
-import FilesRowContainer from "./FilesRow/FilesRowContainer";
-import FilesTileContainer from "./FilesTile/FilesTileContainer";
-import EmptyContainer from "./EmptyContainer";
+import FilesRowContainer from "./RowsView/FilesRowContainer";
+import FilesTileContainer from "./TilesView/FilesTileContainer";
+import EmptyContainer from "../../../../components/EmptyContainer";
+
+import withLoader from "../../../../HOCs/withLoader";
let currentDroppable = null;
-let loadTimeout = null;
-
const SectionBodyContent = (props) => {
const {
t,
tReady,
fileActionId,
viewAs,
- firstLoad,
- isLoading,
isEmptyFilesList,
folderId,
dragging,
@@ -31,31 +28,6 @@ const SectionBodyContent = (props) => {
moveDragItems,
} = props;
- const [inLoad, setInLoad] = useState(false);
-
- const cleanTimer = () => {
- loadTimeout && clearTimeout(loadTimeout);
- loadTimeout = null;
- };
-
- useEffect(() => {
- if (isLoading) {
- cleanTimer();
- loadTimeout = setTimeout(() => {
- console.log("inLoad", true);
- setInLoad(true);
- }, 500);
- } else {
- cleanTimer();
- console.log("inLoad", false);
- setInLoad(false);
- }
-
- return () => {
- cleanTimer();
- };
- }, [isLoading]);
-
useEffect(() => {
const customScrollElm = document.querySelector(
"#customScrollBar > .scroll-body"
@@ -174,11 +146,7 @@ const SectionBodyContent = (props) => {
//console.log("Files Home SectionBodyContent render", props);
return (!fileActionId && isEmptyFilesList) || null ? (
- firstLoad || (isMobile && inLoad) ? (
-
- ) : (
-
- )
+
) : viewAs === "tile" ? (
) : (
@@ -194,14 +162,12 @@ export default inject(
filesActionsStore,
}) => {
const {
- firstLoad,
fileActionStore,
filesList,
dragging,
setDragging,
startDrag,
setStartDrag,
- isLoading,
viewAs,
setTooltipPosition,
} = filesStore;
@@ -209,9 +175,7 @@ export default inject(
return {
dragging,
fileActionId: fileActionStore.id,
- firstLoad,
viewAs,
- isLoading,
isEmptyFilesList: filesList.length <= 0,
setDragging,
startDrag,
@@ -222,4 +186,6 @@ export default inject(
moveDragItems: filesActionsStore.moveDragItems,
};
}
-)(withRouter(withTranslation("Home")(observer(SectionBodyContent))));
+)(
+ withRouter(withTranslation("Home")(withLoader(observer(SectionBodyContent))))
+);
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Filter/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Filter/index.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Filter/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Header/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Header/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Paging/index.js b/products/ASC.Files/Client/src/pages/Home/Section/Paging/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/Paging/index.js
rename to products/ASC.Files/Client/src/pages/Home/Section/Paging/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/index.js b/products/ASC.Files/Client/src/pages/Home/Section/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Home/Section/index.js
rename to products/ASC.Files/Client/src/pages/Home/Section/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Home/index.js b/products/ASC.Files/Client/src/pages/Home/index.js
similarity index 98%
rename from products/ASC.Files/Client/src/components/pages/Home/index.js
rename to products/ASC.Files/Client/src/pages/Home/index.js
index e5fb22e8d3..4683c599c0 100644
--- a/products/ASC.Files/Client/src/components/pages/Home/index.js
+++ b/products/ASC.Files/Client/src/pages/Home/index.js
@@ -14,7 +14,7 @@ import {
ArticleBodyContent,
ArticleHeaderContent,
ArticleMainButtonContent,
-} from "../../Article";
+} from "../../components/Article";
import {
SectionBodyContent,
SectionFilterContent,
@@ -22,11 +22,11 @@ import {
SectionPagingContent,
} from "./Section";
-import { ConvertDialog } from "../../dialogs";
+import { ConvertDialog } from "../../components/dialogs";
import MediaViewer from "./MediaViewer";
-import DragTooltip from "../../DragTooltip";
+import DragTooltip from "../../components/DragTooltip";
import { observer, inject } from "mobx-react";
-import config from "../../../../package.json";
+import config from "../../../package.json";
class PureHome extends React.Component {
componentDidMount() {
diff --git a/products/ASC.Files/Client/src/components/pages/Settings/Section/Body/ConnectedClouds.js b/products/ASC.Files/Client/src/pages/Settings/Section/Body/ConnectedClouds.js
similarity index 90%
rename from products/ASC.Files/Client/src/components/pages/Settings/Section/Body/ConnectedClouds.js
rename to products/ASC.Files/Client/src/pages/Settings/Section/Body/ConnectedClouds.js
index 37cffb9dba..f2d61d9a14 100644
--- a/products/ASC.Files/Client/src/components/pages/Settings/Section/Body/ConnectedClouds.js
+++ b/products/ASC.Files/Client/src/pages/Settings/Section/Body/ConnectedClouds.js
@@ -7,22 +7,22 @@ import Box from "@appserver/components/box";
import Row from "@appserver/components/row";
import RowContainer from "@appserver/components/row-container";
import { withTranslation } from "react-i18next";
-import EmptyFolderContainer from "../../../Home/Section/Body/EmptyContainer/EmptyContainer";
-import BoxIcon from "../../../../../../public/images/icon_box.react.svg";
-import DropBoxIcon from "../../../../../../public/images/icon_dropbox.react.svg";
-import GoogleDriveIcon from "../../../../../../public/images/icon_google_drive.react.svg";
-import KDriveIcon from "../../../../../../public/images/icon_kdrive.react.svg";
-import NextCloudIcon from "../../../../../../public/images/icon_nextcloud.react.svg";
-import OneDriveIcon from "../../../../../../public/images/icon_onedrive.react.svg";
-import OwnCloudIcon from "../../../../../../public/images/icon_owncloud.react.svg";
-import SharePointIcon from "../../../../../../public/images/icon_sharepoint.react.svg";
-import WebDavIcon from "../../../../../../public/images/icon_webdav.react.svg";
-import YandexDiskIcon from "../../../../../../public/images/icon_yandex_disk.react.svg";
+import EmptyFolderContainer from "../../../../components/EmptyContainer/EmptyContainer";
+import BoxIcon from "../../../../../public/images/icon_box.react.svg";
+import DropBoxIcon from "../../../../../public/images/icon_dropbox.react.svg";
+import GoogleDriveIcon from "../../../../../public/images/icon_google_drive.react.svg";
+import KDriveIcon from "../../../../../public/images/icon_kdrive.react.svg";
+import NextCloudIcon from "../../../../../public/images/icon_nextcloud.react.svg";
+import OneDriveIcon from "../../../../../public/images/icon_onedrive.react.svg";
+import OwnCloudIcon from "../../../../../public/images/icon_owncloud.react.svg";
+import SharePointIcon from "../../../../../public/images/icon_sharepoint.react.svg";
+import WebDavIcon from "../../../../../public/images/icon_webdav.react.svg";
+import YandexDiskIcon from "../../../../../public/images/icon_yandex_disk.react.svg";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import { inject, observer } from "mobx-react";
import combineUrl from "@appserver/common/utils/combineUrl";
import AppServerConfig from "@appserver/common/constants/AppServerConfig";
-import config from "../../../../../../package.json";
+import config from "../../../../../package.json";
import { withRouter } from "react-router";
const StyledBoxIcon = styled(BoxIcon)`
diff --git a/products/ASC.Files/Client/src/components/pages/Settings/Section/Body/index.js b/products/ASC.Files/Client/src/pages/Settings/Section/Body/index.js
similarity index 98%
rename from products/ASC.Files/Client/src/components/pages/Settings/Section/Body/index.js
rename to products/ASC.Files/Client/src/pages/Settings/Section/Body/index.js
index 6ee1b38286..9e31b08160 100644
--- a/products/ASC.Files/Client/src/components/pages/Settings/Section/Body/index.js
+++ b/products/ASC.Files/Client/src/pages/Settings/Section/Body/index.js
@@ -6,7 +6,7 @@ import Error403 from "studio/Error403";
import Error520 from "studio/Error520";
import ConnectClouds from "./ConnectedClouds";
import { inject, observer } from "mobx-react";
-import { loopTreeFolders } from "../../../../../helpers/files-helpers";
+import { loopTreeFolders } from "../../../../helpers/files-helpers";
const StyledSettings = styled.div`
display: grid;
diff --git a/products/ASC.Files/Client/src/components/pages/Settings/Section/Header/index.js b/products/ASC.Files/Client/src/pages/Settings/Section/Header/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Settings/Section/Header/index.js
rename to products/ASC.Files/Client/src/pages/Settings/Section/Header/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Settings/Section/index.js b/products/ASC.Files/Client/src/pages/Settings/Section/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/Settings/Section/index.js
rename to products/ASC.Files/Client/src/pages/Settings/Section/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/Settings/index.js b/products/ASC.Files/Client/src/pages/Settings/index.js
similarity index 96%
rename from products/ASC.Files/Client/src/components/pages/Settings/index.js
rename to products/ASC.Files/Client/src/pages/Settings/index.js
index b96270289c..21a3f1667f 100644
--- a/products/ASC.Files/Client/src/components/pages/Settings/index.js
+++ b/products/ASC.Files/Client/src/pages/Settings/index.js
@@ -7,10 +7,10 @@ import {
ArticleHeaderContent,
ArticleBodyContent,
ArticleMainButtonContent,
-} from "../../Article";
+} from "../../components/Article";
import { SectionHeaderContent, SectionBodyContent } from "./Section";
import { withTranslation } from "react-i18next";
-import { setDocumentTitle } from "../../../helpers/utils";
+import { setDocumentTitle } from "../../helpers/utils";
import { inject, observer } from "mobx-react";
const PureSettings = ({
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/StyledVersionRow.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/StyledVersionRow.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/StyledVersionRow.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/StyledVersionRow.js
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/VersionBadge.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/VersionBadge.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/VersionBadge.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/VersionBadge.js
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/VersionRow.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/VersionRow.js
similarity index 98%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/VersionRow.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/VersionRow.js
index dc967ef89d..d7af931dae 100644
--- a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/VersionRow.js
+++ b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/VersionRow.js
@@ -10,7 +10,7 @@ import { withTranslation } from "react-i18next";
import { withRouter } from "react-router";
import VersionBadge from "./VersionBadge";
import StyledVersionRow from "./StyledVersionRow";
-import ExternalLinkIcon from "../../../../../../public/images/external.link.react.svg";
+import ExternalLinkIcon from "../../../../../public/images/external.link.react.svg";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import { inject, observer } from "mobx-react";
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/index.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Body/index.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/Body/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Header/index.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/Header/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/Header/index.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/Header/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/Section/index.js b/products/ASC.Files/Client/src/pages/VersionHistory/Section/index.js
similarity index 100%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/Section/index.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/Section/index.js
diff --git a/products/ASC.Files/Client/src/components/pages/VersionHistory/index.js b/products/ASC.Files/Client/src/pages/VersionHistory/index.js
similarity index 98%
rename from products/ASC.Files/Client/src/components/pages/VersionHistory/index.js
rename to products/ASC.Files/Client/src/pages/VersionHistory/index.js
index c7b55ce021..760d71fda8 100644
--- a/products/ASC.Files/Client/src/components/pages/VersionHistory/index.js
+++ b/products/ASC.Files/Client/src/pages/VersionHistory/index.js
@@ -8,7 +8,7 @@ import {
ArticleHeaderContent,
ArticleBodyContent,
ArticleMainButtonContent,
-} from "../../Article";
+} from "../../components/Article";
import { SectionHeaderContent, SectionBodyContent } from "./Section";
//import { setDocumentTitle } from "../../../helpers/utils";
import { inject, observer } from "mobx-react";
diff --git a/products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs b/products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs
index 5df944c3d1..c9201245e1 100644
--- a/products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs
+++ b/products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs
@@ -976,10 +976,12 @@ namespace ASC.Files.Core.Data
};
FilesDbContext.AddOrUpdate(r => r.BunchObjects, toInsert);
+ FilesDbContext.SaveChanges();
+
tx.Commit(); //Commit changes
}
- FilesDbContext.SaveChanges();
+
return newFolderId;
}
diff --git a/products/ASC.People/Client/src/pages/Profile/Section/Body/index.js b/products/ASC.People/Client/src/pages/Profile/Section/Body/index.js
index deeb6b2674..9287dd6acb 100644
--- a/products/ASC.People/Client/src/pages/Profile/Section/Body/index.js
+++ b/products/ASC.People/Client/src/pages/Profile/Section/Body/index.js
@@ -193,6 +193,7 @@ class SectionBodyContent extends React.PureComponent {
const providerButtons =
providers &&
providers.map((item) => {
+ if (!providersData[item.provider]) return;
const { icon, label, iconOptions } = providersData[item.provider];
if (!icon || !label) return ;
@@ -247,17 +248,21 @@ class SectionBodyContent extends React.PureComponent {
return providerButtons;
};
+ oauthDataExists = () => {
+ const { providers } = this.props;
+
+ let existProviders = 0;
+ providers && providers.length > 0;
+ providers.map((item) => {
+ if (!providersData[item.provider]) return;
+ existProviders++;
+ });
+
+ return !!existProviders;
+ };
+
render() {
- const {
- profile,
- cultures,
- culture,
- isAdmin,
- viewer,
- t,
- isSelf,
- providers,
- } = this.props;
+ const { profile, cultures, culture, isAdmin, t, isSelf } = this.props;
const contacts = profile.contacts && getUserContacts(profile.contacts);
const role = getUserRole(profile);
@@ -300,7 +305,7 @@ class SectionBodyContent extends React.PureComponent {
culture={culture}
/>
- {isSelf && providers && providers.length > 0 && (
+ {isSelf && this.oauthDataExists() && (
diff --git a/web/ASC.Web.Api/Controllers/SettingsController.cs b/web/ASC.Web.Api/Controllers/SettingsController.cs
index 6f4f1ffb32..3b218ff60e 100644
--- a/web/ASC.Web.Api/Controllers/SettingsController.cs
+++ b/web/ASC.Web.Api/Controllers/SettingsController.cs
@@ -1372,21 +1372,54 @@ namespace ASC.Api.Settings
return FirstTimeTenantSettings.SaveData(wizardModel);
}
+ [Read("tfaapp")]
+ public IEnumerable GetTfaSettings()
+ {
+ var result = new List();
+
+ var SmsVisible = StudioSmsNotificationSettingsHelper.IsVisibleSettings();
+ var SmsEnable = SmsVisible && SmsProviderManager.Enabled();
+ var TfaVisible = TfaAppAuthSettings.IsVisibleSettings;
+
+ if (SmsVisible)
+ {
+ result.Add(new TfaSettings
+ {
+ Enabled = StudioSmsNotificationSettingsHelper.Enable,
+ Id = "sms",
+ Title = Resource.ButtonSmsEnable,
+ Avaliable = SmsEnable
+ });
+ }
+
+ if (TfaVisible)
+ {
+ result.Add(new TfaSettings
+ {
+ Enabled = SettingsManager.Load().EnableSetting,
+ Id = "app",
+ Title = Resource.ButtonTfaAppEnable,
+ Avaliable = true
+ });
+ }
+
+ return result;
+ }
[Update("tfaapp")]
public bool TfaSettingsFromBody([FromBody]TfaModel model)
{
- return TfaSettings(model);
+ return TfaSettingsUpdate(model);
}
[Update("tfaapp")]
[Consumes("application/x-www-form-urlencoded")]
public bool TfaSettingsFromForm([FromForm] TfaModel model)
{
- return TfaSettings(model);
+ return TfaSettingsUpdate(model);
}
- private bool TfaSettings(TfaModel model)
+ private bool TfaSettingsUpdate(TfaModel model)
{
PermissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
diff --git a/web/ASC.Web.Api/Models/TfaSettings.cs b/web/ASC.Web.Api/Models/TfaSettings.cs
new file mode 100644
index 0000000000..a5738aea0d
--- /dev/null
+++ b/web/ASC.Web.Api/Models/TfaSettings.cs
@@ -0,0 +1,10 @@
+namespace ASC.Web.Api.Models
+{
+ public class TfaSettings
+ {
+ public string Id { get; set; }
+ public string Title { get; set; }
+ public bool Enabled { get; set; }
+ public bool Avaliable { get; set; }
+ }
+}
diff --git a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js
index 53b92daaa0..b4c8755c1e 100644
--- a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js
+++ b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js
@@ -195,10 +195,11 @@ class Confirm extends React.PureComponent {
const providerButtons =
providers &&
providers.map((item, index) => {
+ if (!providersData[item.provider]) return;
const { icon, label, iconOptions, className } = providersData[
item.provider
];
- if (!icon) return;
+
if (item.provider === "Facebook") {
facebookIndex = index;
return;
@@ -223,6 +224,19 @@ class Confirm extends React.PureComponent {
return providerButtons;
};
+ oauthDataExists = () => {
+ const { providers } = this.props;
+
+ let existProviders = 0;
+ providers && providers.length > 0;
+ providers.map((item) => {
+ if (!providersData[item.provider]) return;
+ existProviders++;
+ });
+
+ return !!existProviders;
+ };
+
authCallback = (profile) => {
const { t, defaultPage } = this.props;
const { FirstName, LastName, EMail, Serialized } = profile;
@@ -493,7 +507,7 @@ class Confirm extends React.PureComponent {
onClick={this.onSubmit}
/>
- {providers && providers.length > 0 && (
+ {this.oauthDataExists && (
{this.providerButtons()}
diff --git a/web/ASC.Web.Core/PublicResources/Resource.Designer.cs b/web/ASC.Web.Core/PublicResources/Resource.Designer.cs
index a50bb4c61d..c3cb8c1481 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.Designer.cs
+++ b/web/ASC.Web.Core/PublicResources/Resource.Designer.cs
@@ -105,6 +105,24 @@ namespace ASC.Web.Core.PublicResources {
}
}
+ ///
+ /// Looks up a localized string similar to By SMS.
+ ///
+ public static string ButtonSmsEnable {
+ get {
+ return ResourceManager.GetString("ButtonSmsEnable", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to By authenticator app.
+ ///
+ public static string ButtonTfaAppEnable {
+ get {
+ return ResourceManager.GetString("ButtonTfaAppEnable", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to A link to confirm the operation has been sent to :email (the email address of the portal owner)..
///
diff --git a/web/ASC.Web.Core/PublicResources/Resource.de.resx b/web/ASC.Web.Core/PublicResources/Resource.de.resx
index a7ce5ef24d..5b51f25a42 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.de.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.de.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Das Feld mit dem Prüfcode darf nicht leer sein
@@ -73,6 +73,12 @@
Ihre Nachricht wurde erfolgreich gesendet. Der Portaladministrator wird Sie kontaktieren.
+
+ Per SMS
+
+
+ Über die Authentifizierungs-App
+
Der Link zum Bestätigen der Operation wurde an :email verschickt (die E-Mail-Adresse des Portalbesitzers).
diff --git a/web/ASC.Web.Core/PublicResources/Resource.es.resx b/web/ASC.Web.Core/PublicResources/Resource.es.resx
index 0af7e35468..6733c1d541 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.es.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.es.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Campo de código de validación no puede estar vacío
@@ -73,6 +73,12 @@
Su mensaje fue enviado con éxito. El administrador del portal se pondrá en contacto con usted.
+
+ Por SMS
+
+
+ Con ayuda de la aplicación para autenticación
+
Un enlace para confirmar la operación ha sido enviada al :correo electrónico (la dirección del correo electrónico del propietario del portal).
diff --git a/web/ASC.Web.Core/PublicResources/Resource.fr.resx b/web/ASC.Web.Core/PublicResources/Resource.fr.resx
index f7b244108b..da2e892699 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.fr.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.fr.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Le champ du code de validation ne peut pas être vide
@@ -73,6 +73,12 @@
Votre message a été envoyé avec succès. Vous serez contacté par l'administrateur du portail.
+
+ par SMS
+
+
+ Par l'application authentificateur
+
Un lien pour confirmer l'opération a été envoyé à :e-mail (l'adresse e-mail du propriétaire du portail).
diff --git a/web/ASC.Web.Core/PublicResources/Resource.it.resx b/web/ASC.Web.Core/PublicResources/Resource.it.resx
index 649248d0d3..c051cfded0 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.it.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.it.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Il campo Valida codice non può essere vuoto
@@ -73,6 +73,12 @@
Il tuo messaggio è stato inviato con successo. Sarai contattato dall'amministratore del portale.
+
+ Tramite SMS
+
+
+ Con authenticator app
+
Un collegamento per confermare l'operazione è stato inviato all'indirizzo :email (l'indirizzo email del proprietario del portale).
diff --git a/web/ASC.Web.Core/PublicResources/Resource.resx b/web/ASC.Web.Core/PublicResources/Resource.resx
index d6a6f2aa69..bf9175aa54 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Validation code field cannot be empty
@@ -73,6 +73,12 @@
Your message was successfully sent. You will be contacted by the portal administrator.
+
+ By SMS
+
+
+ By authenticator app
+
A link to confirm the operation has been sent to :email (the email address of the portal owner).
diff --git a/web/ASC.Web.Core/PublicResources/Resource.ru.resx b/web/ASC.Web.Core/PublicResources/Resource.ru.resx
index 1ddb41e5c2..c7722cb4ee 100644
--- a/web/ASC.Web.Core/PublicResources/Resource.ru.resx
+++ b/web/ASC.Web.Core/PublicResources/Resource.ru.resx
@@ -53,10 +53,10 @@
2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.4.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.5.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Поле кода подтверждения не может быть пустым
@@ -73,6 +73,12 @@
Ваше сообщение успешно отправлено. Администратор портала с Вами свяжется.
+
+ С помощью SMS
+
+
+ С помощью приложения для аутентификации
+
Ссылка для подтверждения операции была отправлена на :email (адрес электронной почты владельца портала).
diff --git a/web/ASC.Web.Login/src/Login.jsx b/web/ASC.Web.Login/src/Login.jsx
index e80f2562b6..ae3dfa97e8 100644
--- a/web/ASC.Web.Login/src/Login.jsx
+++ b/web/ASC.Web.Login/src/Login.jsx
@@ -362,10 +362,12 @@ const Form = (props) => {
const providerButtons =
providers &&
providers.map((item, index) => {
+ if (!providersData[item.provider]) return;
+
const { icon, label, iconOptions, className } = providersData[
item.provider
];
- if (!icon) return;
+
if (item.provider === "Facebook") {
facebookIndex = index;
return;
@@ -390,6 +392,17 @@ const Form = (props) => {
return providerButtons;
};
+ const oauthDataExists = () => {
+ let existProviders = 0;
+ providers && providers.length > 0;
+ providers.map((item) => {
+ if (!providersData[item.provider]) return;
+ existProviders++;
+ });
+
+ return !!existProviders;
+ };
+
//console.log("Login render");
return (
@@ -507,7 +520,7 @@ const Form = (props) => {
)}
- {providers && providers.length > 0 && (
+ {oauthDataExists() && (
<>