Merge branch 'feature/virtual-rooms-1.2' into feature/connected-clouds-redesign

This commit is contained in:
Alexey Safronov 2022-04-18 15:05:23 +03:00
commit 47ffc849a5
123 changed files with 2939 additions and 2965 deletions

View File

@ -1,5 +1,5 @@
REM echo ######## Set variables ########
set "msbuild4="C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe""
set "publisher="Ascensio System SIA""
REM echo ######## Extracting and preparing files to build ########
%sevenzip% x build\install\win\nginx-1.21.1.zip -o"build\install\win\Files" -y
@ -29,7 +29,7 @@ del /f /q "build\install\win\kafka-zookeeper\zookeeper\conf\zoo_sample.cfg"
rmdir build\install\win\publish /s /q
REM echo ######## Build Utils ########
%msbuild4% build\install\win\CustomActions\C#\Utils\Utils.csproj
%msbuild% build\install\win\CustomActions\C#\Utils\Utils.csproj
copy build\install\win\CustomActions\C#\Utils\bin\Debug\Utils.CA.dll build\install\win\Utils.CA.dll /y
rmdir build\install\win\CustomActions\C#\Utils\bin /s /q
rmdir build\install\win\CustomActions\C#\Utils\obj /s /q
@ -80,7 +80,7 @@ copy "build\install\win\publish\Apache ZooKeeper.msi" "build\install\win\Apache
copy "build\install\win\publish\Apache Kafka.msi" "build\install\win\Apache Kafka.msi" /y
REM echo ######## Build MySQL Server Installer ########
iscc "build\install\win\MySQL Server Installer Runner.iss"
iscc /Qp /S"byparam="signtool" sign /a /n "%publisher%" /t http://timestamp.digicert.com $f" "build\install\win\MySQL Server Installer Runner.iss"
REM echo ######## Build AppServer package ########
%AdvancedInstaller% /edit build\install\win\AppServer.aip /SetVersion %BUILD_VERSION%.%BUILD_NUMBER%

View File

@ -175,7 +175,7 @@ namespace ASC.Core
public UserInfo GetUserBySid(string sid)
{
return GetUsersInternal()
.FirstOrDefault(u => u.Sid != null && string.Equals(u.Sid , sid, StringComparison.CurrentCultureIgnoreCase)) ?? Constants.LostUser;
.FirstOrDefault(u => u.Sid != null && string.Equals(u.Sid, sid, StringComparison.CurrentCultureIgnoreCase)) ?? Constants.LostUser;
}
public UserInfo GetSsoUserByNameId(string nameId)
@ -518,6 +518,11 @@ namespace ASC.Core
{
var group = UserService.GetGroup(Tenant.TenantId, groupID);
if (group == null)
{
group = ToGroup(Constants.BuildinGroups.FirstOrDefault(r => r.ID == groupID));
}
return new GroupInfo
{
ID = group.Id,

View File

@ -275,6 +275,7 @@ namespace ASC.Security.Cryptography
case ConfirmType.PhoneAuth:
case ConfirmType.TfaActivation:
case ConfirmType.TfaAuth:
case ConfirmType.Auth:
checkKeyResult = Provider.ValidateEmailKey(email + type, key, Provider.ValidAuthKeyInterval);
break;

View File

@ -43,7 +43,7 @@ namespace ASC.IPSecurity
public IPRestrictionsServiceCache(ICacheNotify<IPRestrictionItem> notify, ICache cache)
{
Cache = cache;
notify.Subscribe((r) => Cache.Remove(GetCacheKey(r.TenantId)), CacheNotifyAction.Any);
notify.Subscribe((r) => Cache.Remove(GetCacheKey(r.TenantId)), CacheNotifyAction.InsertOrUpdate);
Notify = notify;
}

View File

@ -554,7 +554,8 @@ namespace ASC.ElasticSearch
while (!string.IsNullOrEmpty(name = TryGetName(expression, out var member)))
{
sourceExprText = "." + name + sourceExprText;
sourceExprText = "." + name + sourceExprText;
expression = member.Expression;
}
if (isList)

View File

@ -3,6 +3,7 @@ import axios from "axios";
import FilesFilter from "./filter";
import { FolderType } from "../../constants";
import find from "lodash/find";
import { getFolderOptions } from "../../utils";
export function openEdit(fileId, version, doc, view) {
const params = []; // doc ? `?doc=${doc}` : "";
@ -48,19 +49,7 @@ export function getFolderPath(folderId) {
}
export function getFolder(folderId, filter) {
if (folderId && typeof folderId === "string") {
folderId = encodeURIComponent(folderId.replace(/\\\\/g, "\\"));
}
const params =
filter && filter instanceof FilesFilter
? `${folderId}?${filter.toApiUrlParams()}`
: folderId;
const options = {
method: "get",
url: `/files/${params}`,
};
const options = getFolderOptions(folderId, filter);
return request(options);
}
@ -181,6 +170,38 @@ export function getFoldersTree() {
);
}
export function getCommonFoldersTree() {
const index = 1;
return request({ method: "get", url: "/files/@common" }).then(
(commonFolders) => {
return [
{
id: commonFolders.current.id,
key: `0-${index}`,
parentId: commonFolders.current.parentId,
title: commonFolders.current.title,
rootFolderType: +commonFolders.current.rootFolderType,
rootFolderName: "@common",
pathParts: commonFolders.pathParts,
foldersCount: commonFolders.current.foldersCount,
newItems: commonFolders.new,
},
];
}
);
}
export function getThirdPartyCommonFolderTree() {
return request({ method: "get", url: "/files/thirdparty/common" }).then(
(commonThirdPartyArray) => {
commonThirdPartyArray.map((currentValue, index) => {
commonThirdPartyArray[index].key = `0-${index}`;
});
return commonThirdPartyArray;
}
);
}
export function getMyFolderList(filter = FilesFilter.getDefault()) {
const options = {
method: "get",

View File

@ -415,13 +415,6 @@ export function validateTfaCode(code) {
});
}
export function getCommonThirdPartyList() {
const options = {
method: "get",
url: "/files/thirdparty/common",
};
return request(options);
}
export function getBackupStorage() {
const options = {
method: "get",

View File

@ -3,7 +3,14 @@ import PropTypes from "prop-types";
import { StyledRow } from "./StyledListLoader";
import RectangleLoader from "../RectangleLoader";
const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
const ListItemLoader = ({
id,
className,
style,
withoutFirstRectangle,
withoutLastRectangle,
...rest
}) => {
const {
title,
borderRadius,
@ -16,8 +23,14 @@ const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
} = rest;
return (
<StyledRow id={id} className={className} style={style}>
{isRectangle && (
<StyledRow
id={id}
className={className}
style={style}
withoutFirstRectangle={withoutFirstRectangle}
withoutLastRectangle={withoutLastRectangle}
>
{!withoutFirstRectangle && (
<RectangleLoader
title={title}
width="16"
@ -60,18 +73,20 @@ const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
animate={animate}
/>
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
/>
{!withoutLastRectangle && (
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
/>
)}
</StyledRow>
);
};
@ -80,14 +95,16 @@ ListItemLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
isRectangle: PropTypes.bool,
withoutFirstRectangle: PropTypes.bool,
withoutLastRectangle: PropTypes.bool,
};
ListItemLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
isRectangle: true,
withoutFirstRectangle: false,
withoutLastRectangle: false,
};
export default ListItemLoader;

View File

@ -9,21 +9,26 @@ const StyledRow = styled.div`
width: 100%;
display: grid;
grid-template-columns: 16px 32px 1fr 16px;
${(props) =>
props.withoutFirstRectangle &&
props.withoutLastRectangle &&
"grid-template-columns: 32px 1fr"};
${(props) =>
props.withoutFirstRectangle &&
!props.withoutLastRectangle &&
"grid-template-columns: 32px 1fr 16px"};
grid-template-rows: 1fr;
grid-column-gap: 8px;
margin-bottom: 16px;
justify-items: center;
align-items: center;
.list-loader_rectangle {
padding-right: 4px;
}
.list-loader_rectangle-content {
width: 32px;
height: 32px;
}
.list-loader_rectangle-row {
margin-right: auto;
max-width: 167px;

View File

@ -0,0 +1,24 @@
import styled from "styled-components";
import { desktop } from "@appserver/components/utils/device";
const StyledTreeFolder = styled.div`
padding-right: 16px;
`;
const StyledLoader = styled.div`
width: 100%;
display: grid;
grid-template-columns: 8px 16px 1fr;
grid-template-rows: 1fr;
grid-column-gap: 6px;
margin-bottom: 8px;
box-sizing: border-box;
.tree-node-loader_additional-rectangle {
padding-top: 4px;
}
${(props) => props.paddingLeft && `padding-left: ${props.paddingLeft}`};
`;
export { StyledLoader, StyledTreeFolder };

View File

@ -0,0 +1,51 @@
import React from "react";
import PropTypes from "prop-types";
import { StyledTreeFolder, StyledLoader } from "./StyledTreeFolder";
import TreeNodeLoader from "../TreeNodeLoader";
const NewTreeFolderLoader = ({ id, className, style, ...rest }) => {
return (
<StyledTreeFolder id={id} className={className} style={style}>
<StyledLoader>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
</StyledTreeFolder>
);
};
NewTreeFolderLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
NewTreeFolderLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
};
export default NewTreeFolderLoader;

View File

@ -11,6 +11,7 @@ const TreeNodeLoader = ({
foregroundOpacity,
speed,
animate,
withRectangle = false,
}) => {
return (
<>
@ -25,6 +26,23 @@ const TreeNodeLoader = ({
speed={speed}
animate={animate}
/>
{withRectangle && (
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
className="tree-node-loader_additional-rectangle"
/>
)}
<RectangleLoader
title={title}
width="100%"

View File

@ -7,6 +7,7 @@ import ArticleButton from "./ArticleButtonLoader";
import ArticleFolder from "./ArticleFolderLoader";
import ArticleGroup from "./ArticleGroupsLoader";
import TreeFolders from "./TreeFolderLoader";
import NewTreeFolders from "./NewTreeFolderLoader";
import TreeSettingsLoader from "./TreeSettingsLoader";
import Row from "./RowLoader";
import Rows from "./RowsLoader";
@ -46,4 +47,5 @@ export default {
ArticleFolder,
ArticleGroup,
ListLoader,
NewTreeFolders,
};

View File

@ -324,3 +324,22 @@ export function convertLanguage(key) {
return key;
}
import FilesFilter from "../api/files/filter";
export function getFolderOptions(folderId, filter) {
if (folderId && typeof folderId === "string") {
folderId = encodeURIComponent(folderId.replace(/\\\\/g, "\\"));
}
const params =
filter && filter instanceof FilesFilter
? `${folderId}?${filter.toApiUrlParams()}`
: folderId;
const options = {
method: "get",
url: `/files/${params}`,
};
return options;
}

View File

@ -37,7 +37,14 @@ Button.propTypes = {
/** Size of button.
The normal size equals 36px and 40px in height on the Desktop and Touchcreen devices. */
size: PropTypes.oneOf(["extraSmall", "small", "normal", "medium"]),
size: PropTypes.oneOf([
"extraSmall",
"small",
"normal",
"medium",
"normalDesktop",
"normalTouchscreen",
]),
/** Scale width of button to 100% */
scale: PropTypes.bool,
/** Icon node element */

View File

@ -177,7 +177,9 @@ class ModalDialog extends React.Component {
<BodyBox paddingProp={modalBodyPadding}>
{body ? body.props.children : null}
</BodyBox>
<Box>{footer ? footer.props.children : null}</Box>
<Box className="modal-dialog-modal-footer">
{footer ? footer.props.children : null}
</Box>
</>
)}
</Content>

View File

@ -108,7 +108,6 @@ RadioButtonGroup.defaultProps = {
selected: undefined,
spacing: "15px",
orientation: "horizontal",
width: "100%",
};
export default RadioButtonGroup;

View File

@ -17,7 +17,7 @@ const StyledDiv = styled(ClearDiv)`
`) ||
(props.orientation === "vertical" &&
css`
display: block;
display: inline-block;
`)};
width: ${(props) => props.width};

View File

@ -519,6 +519,7 @@ const Base = {
minHeight: "47px",
width: "100%",
borderBottom: globalColors.grayLightMid,
backgroundColor: globalColors.lightHover,
minWidth: "160px",
overflow: "hidden",
textOverflow: "ellipsis",
@ -1930,7 +1931,6 @@ const Base = {
menuContainer: {
background: "linear-gradient(200.71deg, #2274aa 0%, #0f4071 100%)",
arrowTop: "#206FA4",
color: white,
},

View File

@ -517,6 +517,7 @@ const Dark = {
minHeight: "47px",
width: "100%",
borderBottom: "#474747",
backgroundColor: globalColors.veryDarkGrey,
minWidth: "160px",
overflow: "hidden",
textOverflow: "ellipsis",
@ -1930,10 +1931,8 @@ const Dark = {
},
menuContainer: {
background:
"linear-gradient(226.22deg, #EBB67A 0.24%, #E9AC6B 11.61%, #E8A25D 22.98%, #E69850 34.34%, #E58D42 45.71%, #E38235 57.08%, #E27628 68.44%, #E06A1B 79.81%)",
arrowTop: "#EAB274",
color: "#22221f",
background: "linear-gradient(226.22deg, #606060 0.24%, #1F1F1F 79.81%)",
color: "rgba(255, 255, 255, 0.92)",
},
article: {

View File

@ -22,6 +22,7 @@ const StyledTreeMenu = styled(Tree)`
.rc-tree-switcher {
margin-left: 0 !important;
padding-left: 16px;
margin-top: 1px;
}
@ -30,7 +31,7 @@ const StyledTreeMenu = styled(Tree)`
width: 18px;
height: 16px;
padding: 0;
margin-top: 4px;
margin-top: -4px;
}
${(props) =>
@ -44,22 +45,22 @@ const StyledTreeMenu = styled(Tree)`
margin-right: 6px !important;
}
.rc-tree-node-content-wrapper {
position: static !important;
margin-bottom: ${(props) => +props.gapBetweenNodes - 16 + "px;"};
padding-top: 4px;
}
${(props) =>
!props.isFullFillSelection &&
css`
span.rc-tree-node-selected {
width: min-content !important;
padding-right: 4px;
// width: min-content !important;
// padding-right: 4px;
max-width: 98%;
}
`}
& .rc-tree-node-selected .rc-tree-title {
${(props) => !props.isFullFillSelection && "width: calc(100% - 26px);"}
${(props) => !props.isFullFillSelection && "width: calc(100% - 16px);"}
margin-top: 2px;
}
&:not(.rc-tree-show-line) .rc-tree-switcher-noop {
@ -84,12 +85,34 @@ const StyledTreeMenu = styled(Tree)`
.rc-tree-child-tree-open {
display: block;
${(props) => props.disableSwitch && "margin: 0 0 25px 0;"}
margin-left: ${(props) => (props.disableSwitch ? "27px" : "8px")};
li:first-child {
margin-top: ${(props) => (props.disableSwitch ? "10px" : "6px")};
margin-top: ${(props) => (props.disableSwitch ? "10px" : "8px")};
margin-bottom: 8px;
margin-left: 0;
}
li {
padding-left: 16px;
}
}
.rc-tree-treenode-selected {
::after {
position: absolute;
display: block;
top: 0px;
width: 100%;
width: ${(props) => props.widthAdditional};
height: 36px;
background-color: #f3f4f4;
content: "";
z-index: 1;
right: -${(props) => props.multiplicationFactor - 4}px;
}
.span.rc-tree-node-selected {
}
}
.rc-tree-treenode-disabled > span:not(.rc-tree-switcher),
.rc-tree-treenode-disabled > a,
.rc-tree-treenode-disabled > a span {
@ -136,12 +159,12 @@ const StyledTreeMenu = styled(Tree)`
: ``}
@media (max-width: 1024px) {
margin-top: 20px !important;
.rc-tree-node-content-wrapper {
/* .rc-tree-node-content-wrapper {
margin-bottom: ${(props) =>
props.gapBetweenNodesTablet
? +props.gapBetweenNodesTablet - 16 + "px;"
: +props.gapBetweenNodes - 16 + "px;"};
}
props.gapBetweenNodesTablet
? +props.gapBetweenNodesTablet - 16 + "px;"
: +props.gapBetweenNodes - 16 + "px;"};
} */
& > li > .rc-tree-child-tree {
margin-left: 4px;
}
@ -151,7 +174,6 @@ const StyledTreeMenu = styled(Tree)`
StyledTreeMenu.defaultProps = { theme: Base };
const TreeMenu = React.forwardRef((props, ref) => {
//console.log("TreeMenu render");
const {
defaultExpandAll,
defaultExpandParent,
@ -193,6 +215,7 @@ const TreeMenu = React.forwardRef((props, ref) => {
gapBetweenNodesTablet,
isEmptyRootNode,
theme,
childrenCount,
} = props;
const expandedKeysProp = expandedKeys ? { expandedKeys: expandedKeys } : {};
@ -277,6 +300,10 @@ const TreeMenu = React.forwardRef((props, ref) => {
gapBetweenNodes={gapBetweenNodes}
gapBetweenNodesTablet={gapBetweenNodesTablet}
isEmptyRootNode={isEmptyRootNode}
widthAdditional={`calc(100% + ${
(childrenCount ? childrenCount : 1) * 32
}px)`}
multiplicationFactor={32}
>
{modifiedChildren}
</StyledTreeMenu>

View File

@ -123,7 +123,8 @@ const TreeNodeMenu = styled(TreeNode)`
${NoUserSelect}
.rc-tree-node-selected {
max-width: ${(props) => (props.newItems > 999 ? "71%" : "102%")} !important;
max-width: ${(props) => (props.newItems > 999 ? "71%" : "87%")} !important;
width: calc(100% - 10px) !important;
}
${(props) =>
@ -182,9 +183,12 @@ const TreeNodeMenu = styled(TreeNode)`
padding: 0;
}
.rc-tree-node-content-wrapper {
width: ${(props) => (props.disableSwitch ? "90%" : "108%")};
// width: ${(props) => (props.disableSwitch ? "90%" : "108%")};
width: calc(100% - 16px);
max-width: 87%;
display: inline-block;
position: relative;
z-index: 2;
/*min-width: ${(props) => (props.disableSwitch ? "160px" : "190px")};*/
// overflow: hidden;
@ -220,6 +224,7 @@ const TreeNodeMenu = styled(TreeNode)`
svg {
height: 16px;
margin-top: 10px;
}
}
span.rc-tree-iconEle {
@ -242,6 +247,8 @@ const TreeNodeMenu = styled(TreeNode)`
vertical-align: 1px;
height: 24px;
width: 8px;
z-index: 2;
position: relative;
}
span.rc-tree-switcher.rc-tree-icon__customize,
span.rc-tree-checkbox.rc-tree-icon__customize,
@ -324,7 +331,7 @@ const TreeNodeMenu = styled(TreeNode)`
? props.icon
? props.newItems > 999
? "calc(100% - 104px)"
: "calc(100% - 44px)"
: "calc(100% - 16px)"
: "calc(100% - 20px)"
: "100%"};
white-space: nowrap;
@ -333,13 +340,14 @@ const TreeNodeMenu = styled(TreeNode)`
color: ${(props) => props.theme.treeNode.title.color};
padding-left: ${(props) =>
props.icon || props.disableSwitch ? "0" : "20px"};
margin-top: 2px;
}
span.rc-tree-title:first-child {
max-width: 100%;
}
.rc-tree-node-selected {
background: ${(props) => props.theme.treeNode.selected.background};
//background: ${(props) => props.theme.treeNode.selected.background};
mix-blend-mode: normal;
border-radius: ${(props) => props.theme.treeNode.selected.borderRadius};
z-index: 0;
@ -354,6 +362,9 @@ const TreeNodeMenu = styled(TreeNode)`
props.theme.treeNode.selected.hoverBackgroundColor};
}
overflow: visible;
z-index: 2;
position: relative;
padding-top: 4px;
}
.newItem {

View File

@ -14,6 +14,8 @@ const globalColors = {
veryLightGrey: "#CACACA",
darkSilver: "#bbb",
silver: "#CCCCCC",
lightHover: "#F3F4F4",
veryDarkGrey: "#3D3D3D",
blueMain: "#2DA7DB",
blueHover: "#3DB8EC",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} adlı fayl artıq {{folder}} qovluğunda mövcuddur. ",
"ConflictResolveDescriptionFiles": "{{filesCount}} sənəd sayı artıq '{{folder}}' qovluğunda mövcuddur.",
"ConflictResolveDescription": "<1>{{file}}</1> adlı fayl artıq <1>{{folder}}</1> qovluğunda mövcuddur. ",
"ConflictResolveDescriptionFiles": "{{filesCount}} sənəd sayı artıq <1>{{folder}}<1> qovluğunda mövcuddur.",
"ConflictResolveSelectAction": "Xahiş edirik əməliyyatı seçin:",
"ConflictResolveTitle": "Üzərinə yazmanın təsdiqlənməsi",
"CreateDescription": "Qovluqda iki fərqli fayl olacaq. ",
@ -9,4 +9,4 @@
"OverwriteTitle": "Versiya yenilənməsi ilə üzərinə yazma",
"SkipDescription": "Heç bir fayl köçürülmədi. Orijinal fayl köçürülən yerdə dəyişikliksiz qaldı.",
"SkipTitle": "Atlamaq"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл с име {{file}} вече съществува в папка {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документа с едно и също име вече съществуват в папката '{{folder}}'.",
"ConflictResolveDescription": "Файл с име <1>{{file}}</1> вече съществува в папка <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документа с едно и също име вече съществуват в папката <1>{{folder}}<1>.",
"ConflictResolveSelectAction": "Моля, изберете действие:",
"ConflictResolveTitle": "Потвърждение за презаписване",
"CreateDescription": "Ще има два различни файла в папката.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Препишете с актуализацията на версията",
"SkipDescription": "Никой файл няма да бъде копиран. Оригиналният файл ще бъде задържан в целевата папка.",
"SkipTitle": "Пропусни"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Soubor s názvem {{file}} již existuje ve složce {{folder}}.",
"ConflictResolveDescriptionFiles": "Ve složce '{{folder}}' již existuje {{filesCount}} dokumentů se stejným názvem.",
"ConflictResolveDescription": "Soubor s názvem <1>{{file}}</1> již existuje ve složce <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "Ve složce <1>{{folder}}<1> již existuje {{filesCount}} dokumentů se stejným názvem.",
"ConflictResolveSelectAction": "Prosím, vyberte akci:",
"ConflictResolveTitle": "Potvrzení o přepsání",
"CreateDescription": "Ve složce budou dva různé soubory.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Přepsat aktualizací verze",
"SkipDescription": "Žádný soubor nebude zkopírován. Původní soubor bude zachován v cílové složce.",
"SkipTitle": "Přeskočit"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Die Datei {{file}} existiert bereits im Ordner {{folder}}",
"ConflictResolveDescriptionFiles": "im Ordner '{{folder}}' sind bereits {{filesCount}} Dokumente mit dem gleichen Namen vorhanden",
"ConflictResolveDescription": "Die Datei <1>{{file}}</1> existiert bereits im Ordner <1>{{folder}}</1>",
"ConflictResolveDescriptionFiles": "im Ordner <1>{{folder}}</1> sind bereits {{filesCount}} Dokumente mit dem gleichen Namen vorhanden",
"ConflictResolveSelectAction": "Bitte wählen Sie die Aktion aus:",
"ConflictResolveTitle": "Bestätigung überschreiben",
"CreateDescription": "Es werden zwei verschiedene Dateien in dem Ordner vorhanden sein.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Überschreiben mit Versions-Update",
"SkipDescription": "Es wird keine Datei kopiert. Die Originaldatei wird im Zielordner beibehalten.",
"SkipTitle": "Überspringen"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Το αρχείο με το όνομα {{file}} υπάρχει ήδη στον φάκελο {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} έγγραφα με το ίδιο όνομα υπάρχουν ήδη στον φάκελο {{folder}}",
"ConflictResolveDescription": "Το αρχείο με το όνομα <1>{{file}}</1> υπάρχει ήδη στον φάκελο <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} έγγραφα με το ίδιο όνομα υπάρχουν ήδη στον φάκελο <1>{{folder}}</1>",
"ConflictResolveSelectAction": "Επιλέξτε την ενέργεια",
"ConflictResolveTitle": "Επιβεβαίωση αντικατάστασης",
"CreateDescription": "Θα υπάρχουν δύο διαφορετικά αρχεία στον φάκελο.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Αντικατάσταση με ενημέρωση έκδοσης",
"SkipDescription": "Δεν θα αντιγραφεί κανένα αρχείο. Το αρχικό αρχείο θα διατηρηθεί στον φάκελο προορισμού.",
"SkipTitle": "Παράλειψη"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "The file with the name {{file}} already exists in the folder {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documents with the same name already exist in the folder '{{folder}}'.",
"ConflictResolveDescription": "The file with the name <1>{{file}}</1> already exists in the folder <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documents with the same name already exist in the folder <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Please select the action:",
"ConflictResolveTitle": "Overwrite confirmation",
"CreateDescription": "There will be two different files in the folder.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overwrite with version update",
"SkipDescription": "No file will be copied. The original file will be retained in the destination folder.",
"SkipTitle": "Skip"
}
}

View File

@ -1,3 +1,4 @@
{
"NotAvailableFolder": "No folders available"
}
"NotAvailableFolder": "No folders available",
"FolderContents": "The contents of the '{{folderTitle}}' folder"
}

View File

@ -4,6 +4,7 @@
"ButtonShareAccess": "Sharing Settings",
"ConnectingAccount": "Connecting account",
"Copy": "Copy",
"CopyHere": "Copy here",
"CreateMasterFormFromFile": "Create Form Template from file",
"DeleteFromTrash": "Selected elements were successfully deleted from Trash",
"DeleteOperation": "Deleting",
@ -11,6 +12,7 @@
"DeleteThirdParty": "Delete third-party",
"DownloadApps": "Download applications",
"DownloadAs": "Download as",
"Documents": "Documents",
"EncryptedFileSaving": "Saving encrypted file",
"Files": "Files",
"FileProtected": "The file is password protected",
@ -33,6 +35,7 @@
"MobileMac": "Download ONLYOFFICE Desktop Editors for Mac OS",
"MobileWin": "Download ONLYOFFICE Desktop Editors for Windows",
"Move": "Move",
"MoveHere": "Move here",
"MoveToOperation": "Moving",
"NewForm": "Form template",
"NewFormFile": "Form from text file",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "El archivo con el nombre {{file}} ya existe en la carpeta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos con el mismo nombre ya existen en la carpeta '{{folder}}'.",
"ConflictResolveDescription": "El archivo con el nombre <1>{{file}}</1> ya existe en la carpeta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos con el mismo nombre ya existen en la carpeta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Por favor, seleccione la acción:",
"ConflictResolveTitle": "Confirmación de sobrescritura",
"CreateDescription": "Habrá dos archivos diferentes en la carpeta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sobrescribir con la actualización de la versión",
"SkipDescription": "No se copiará ningún archivo. El archivo original se conservará en la carpeta de destino.",
"SkipTitle": "Omitir"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Tiedosto, jonka nimi on {{file}}, on jo kansiossa {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} samannimistä asiakirjaa on jo olemassa kansiossa {{folder}}.",
"ConflictResolveDescription": "Tiedosto, jonka nimi on <1>{{file}}</1>, on jo kansiossa <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} samannimistä asiakirjaa on jo olemassa kansiossa <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Valitse toiminto:",
"ConflictResolveTitle": "Korvaamisen vahvistus",
"CreateDescription": "Kansiossa tulee olemaan kaksi eri tiedostoa.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Korvaa versiopäivityksellä",
"SkipDescription": "Mitään tiedostoa ei kopioida. Alkuperäinen tiedosto säilytetään kohdekansiossa.",
"SkipTitle": "Ohita"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Le fichier portant le nom {{file}} existe déjà dans le dossier {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} les documents portant le même nom existent déjà dans le dossier '{{folder}}'.",
"ConflictResolveDescription": "Le fichier portant le nom <1>{{file}}</1> existe déjà dans le dossier <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} les documents portant le même nom existent déjà dans le dossier <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Veuillez sélectionner une action :",
"ConflictResolveTitle": "Confirmation de l'écrasement",
"CreateDescription": "Il y aura deux fichiers différents dans le dossier.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Écraser avec la mise à jour de la version",
"SkipDescription": "Aucun fichier ne sera copié. Le fichier d'origine sera conservé dans le dossier de destination.",
"SkipTitle": "Sauter"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Il file con il nome {{file}} già esiste nella cartella {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenti con lo stesso nome esistono già nella cartella '{{folder}}'.",
"ConflictResolveDescription": "Il file con il nome <1>{{file}}</1> già esiste nella cartella <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenti con lo stesso nome esistono già nella cartella <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Ti preghiamo di selezionare l'azione:",
"ConflictResolveTitle": "Confermare sovrascrittura",
"CreateDescription": "Ci saranno due file diversi nella cartella.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sovrascrivere con l'aggiornamento della versione",
"SkipDescription": "Nessun file verrà copiato. Il file originale verrà conservato nella cartella di destinazione.",
"SkipTitle": "Saltare"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "フォルダ{{folder}}内に{{file}}という名前のファイルがすでに存在しています。",
"ConflictResolveDescriptionFiles": "{{filesCount}}フォルダ'{{folder}}'に同名のドキュメントがすでに存在しています。",
"ConflictResolveDescription": "フォルダ<1>{{folder}}</1>内に<1>{{file}}</1>という名前のファイルがすでに存在しています。",
"ConflictResolveDescriptionFiles": "{{filesCount}}フォルダ<1>{{folder}}</1>に同名のドキュメントがすでに存在しています。",
"ConflictResolveSelectAction": "アクションをお選びください:",
"ConflictResolveTitle": "上書き確認",
"CreateDescription": "フォルダ内には2種類のファイルが存在します。",
@ -9,4 +9,4 @@
"OverwriteTitle": "バージョンアップによる上書き",
"SkipDescription": "ファイルはコピーされません。元のファイルはコピー先のフォルダーに保持されます。",
"SkipTitle": "スキップ"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} 이름의 파일이 {{folder}} 폴더에 이미 존재합니다.",
"ConflictResolveDescriptionFiles": "같은 이름의 문서 {{filesCount}}개가 '{{folder}}' 폴더에 이미 존재합니다.",
"ConflictResolveDescription": "<1>{{file}}</1> 이름의 파일이 <1>{{folder}}</1> 폴더에 이미 존재합니다.",
"ConflictResolveDescriptionFiles": "같은 이름의 문서 {{filesCount}}개가 <1>{{folder}}</1> 폴더에 이미 존재합니다.",
"ConflictResolveSelectAction": "작업 선택:",
"ConflictResolveTitle": "덮어쓰기",
"CreateDescription": "폴더에 두 개의 다른 파일이 저장됩니다.",
@ -9,4 +9,4 @@
"OverwriteTitle": "버전 업데이트로 덮어쓰기",
"SkipDescription": "파일이 복사되지 않습니다. 원본 파일이 대상 폴더에 유지됩니다.",
"SkipTitle": "건너뛰기"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "ຊື່ໄຟລ໌ {{file}} ມີຢູ່ໃນໂຟນເດີ {{folder}} ແລ້ວ.",
"ConflictResolveDescriptionFiles": "{{filesCount}} ມີເອກະສານຊື່ດ່ຽວກັນໃນໂໄນເດີ້ແລ້ວ '{{folder}}'.",
"ConflictResolveDescription": "ຊື່ໄຟລ໌ <1>{{file}}</1> ມີຢູ່ໃນໂຟນເດີ <1>{{folder}}</1> ແລ້ວ.",
"ConflictResolveDescriptionFiles": "{{filesCount}} ມີເອກະສານຊື່ດ່ຽວກັນໃນໂໄນເດີ້ແລ້ວ <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "ກະລຸນາເລືອກການດຳເນີນການ",
"ConflictResolveTitle": "ຢືນຢັນການທັບ",
"CreateDescription": "ໃນໂຟນເດີ້ຈະມີ 2 ໄຟລ໌ແຕກຕ່າງກັນ.",
@ -9,4 +9,4 @@
"OverwriteTitle": "ອັບເດດທັບເວີຊັ່ນເກົ່າ",
"SkipDescription": "ໄຟລ໌ຈະບໍ່ຖືກສຳເນົາ ໄຟລ໌ຕົ້ນສະບັບຈະຖືກເກັບໄວ້ໃນໂຟນເດີ້.",
"SkipTitle": "ຂ້າມ"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Fails ar nosaukumu {{file}} jau pastāv mapē {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumenti ar tādu pašu nosaukumu jau pastāv mapē '{{folder}}'.",
"ConflictResolveDescription": "Fails ar nosaukumu <1>{{file}}</1> jau pastāv mapē <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumenti ar tādu pašu nosaukumu jau pastāv mapē <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Lūdzu, atlasiet darbību:",
"ConflictResolveTitle": "Pārrakstiet apstiprinājumu",
"CreateDescription": "Mapē būs divi dažādi faili.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overwrite with version update",
"SkipDescription": "Fails tiks pievienots failam ar tādu pašu nosaukumu kā versijai.",
"SkipTitle": "Izlaist"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Het bestand met de naam {{file}} bestaat al in de map {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenten met dezelfde naam al bestaan in de map '{{folder}}'.",
"ConflictResolveDescription": "Het bestand met de naam <1>{{file}}</1> bestaat al in de map <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenten met dezelfde naam al bestaan in de map <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Kies de actie:",
"ConflictResolveTitle": "Overschrijf bevestiging",
"CreateDescription": "Er zullen twee verschillende bestanden in de map staan.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overschrijven met versie update",
"SkipDescription": "Er wordt geen bestand gekopieerd. Het originele bestand blijft bewaard in de doelmap.",
"SkipTitle": "Overslaan"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Plik o nazwie {{file}} istnieje już w folderze {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokument(y/ów) o tej samej nazwie istnieje(-ją) już w folderze '{{folder}}'.",
"ConflictResolveDescription": "Plik o nazwie <1>{{file}}</1> istnieje już w folderze <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokument(y/ów) o tej samej nazwie istnieje(-ją) już w folderze <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Wybierz działanie:",
"ConflictResolveTitle": "Potwierdź zastąpienie",
"CreateDescription": "W folderze będą dwa różne pliki.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Zastąp i zaktualizuj wersję",
"SkipDescription": "Żaden plik nie zostanie skopiowany. Oryginalny plik zostanie zachowany w folderze docelowym.",
"SkipTitle": "Pomiń"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "O arquivo com o nome {{file}} já existe na pasta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} Os documentos com o mesmo nome já existem na pasta '{{folder}}.",
"ConflictResolveDescription": "O arquivo com o nome <1>{{file}}</1> já existe na pasta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} Os documentos com o mesmo nome já existem na pasta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selecione a ação:",
"ConflictResolveTitle": "Confirmação de substituição",
"CreateDescription": "Haverão dois arquivos diferentes na pasta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Substituir por versão atualizada",
"SkipDescription": "Nenhum arquivo será copiado. O arquivo original será mantido na pasta de destino.",
"SkipTitle": "Pular"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "O ficheiro com o nome {{file}} já existe na pasta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos com o mesmo nome já existem na pasta '{{folder}}.",
"ConflictResolveDescription": "O ficheiro com o nome <1>{{file}}</1> já existe na pasta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos com o mesmo nome já existem na pasta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selecione a ação:",
"ConflictResolveTitle": "Confirmação de substituição",
"CreateDescription": "Haverá dois ficheiros diferentes na pasta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Substituir por versão atualizada",
"SkipDescription": "Nenhum ficheiro será copiado. O ficheiro original será mantido na pasta de destino.",
"SkipTitle": "Ignorar"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Fișierul cu numele {{file}} există deja în dosarul {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} fișiere cu același nume exista deja în folderul'{{folder}}'.",
"ConflictResolveDescription": "Fișierul cu numele <1>{{file}}</1> există deja în dosarul <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} fișiere cu același nume exista deja în folderul<1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selectați acțiune:",
"ConflictResolveTitle": "Confirmarea suprascrierii",
"CreateDescription": "Două fișiere diferite vor fi salvate în dosarul.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Suprascriere la o versiune actualizată",
"SkipDescription": "Niciun fișier n-o să fie copiat. Fișierul de origine rămâne în dosarul de destinație.",
"SkipTitle": "Treci mai departe"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл с именем {{file}} уже существует в папке {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документов с одинаковыми именами уже существуют в папке '{{folder}}'.",
"ConflictResolveDescription": "Файл с именем <1>{{file}}</1> уже существует в папке <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документов с одинаковыми именами уже существуют в папке <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Пожалуйста, выберите действие:",
"ConflictResolveTitle": "Подтверждение перезаписи",
"CreateDescription": "В папке будет два разных файла.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Перезаписать с обновлением версии",
"SkipDescription": "Файл не будет скопирован. В папке назначения останется исходный файл.",
"SkipTitle": "Пропустить"
}
}

View File

@ -1,3 +1,4 @@
{
"NotAvailableFolder": "Нет доступных папок"
}
"NotAvailableFolder": "Нет доступных папок",
"FolderContents": "Содержимое папки '{{folderTitle}}'"
}

View File

@ -4,6 +4,7 @@
"ButtonShareAccess": "Настройка доступа",
"ConnectingAccount": "Подключение аккаунта",
"Copy": "Копировать",
"CopyHere": "Копировать сюда",
"CreateMasterFormFromFile": "Создать шаблон формы из файла",
"DeleteFromTrash": "Выбранные элементы успешно удалены из корзины",
"DeleteOperation": "Удаление",
@ -11,6 +12,7 @@
"DeleteThirdParty": "Отключить сторонний ресурс",
"DownloadApps": "Скачать приложения",
"DownloadAs": "Скачать как",
"Documents": "Документы",
"EncryptedFileSaving": "Сохранение зашифрованного файла",
"Files": "Файлы",
"FileProtected": "Файл защищен с помощью пароля",
@ -33,6 +35,7 @@
"MobileMac": "Скачать десктопные редакторы ONLYOFFICE для Mac OS",
"MobileWin": "Скачать десктопные редакторы ONLYOFFICE для Windows",
"Move": "Переместить",
"MoveHere": "Переместить сюда",
"MoveToOperation": "Перемещение",
"NewForm": "Шаблон формы",
"NewFormFile": "Форма из текстового файла",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Súbor s názvom {{file}} už v priečinku {{folder}} existuje.",
"ConflictResolveDescriptionFiles": "Dokumenty {{filesCount}} s rovnakými názvami už v priečinku '{{folder}}' existujú.",
"ConflictResolveDescription": "Súbor s názvom <1>{{file}}</1> už v priečinku <1>{{folder}}</1> existuje.",
"ConflictResolveDescriptionFiles": "Dokumenty {{filesCount}} s rovnakými názvami už v priečinku <1>{{folder}}</1> existujú.",
"ConflictResolveSelectAction": "Vyberte akciu:",
"ConflictResolveTitle": "Potvrdenie prepísania",
"CreateDescription": "V priečinku budú dva rôzne súbory.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Prepísať a aktualizovať verziu",
"SkipDescription": "Žiadny súbor nebude skopírovaný. Pôvodný súbor zostane v cieľovom priečinku.",
"SkipTitle": "Preskočiť"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Datoteka z imenom {{file}} že obstaja v mapi {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumentov z enakim imenom že obstaja v mapi '{{folder}}'.",
"ConflictResolveDescription": "Datoteka z imenom <1>{{file}}</1> že obstaja v mapi <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumentov z enakim imenom že obstaja v mapi <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Prosim, izberite akcijo:",
"ConflictResolveTitle": "Prepišite potrditev",
"CreateDescription": "V mapi bosta dve različni datoteki.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Prepišite s posodobitvijo različice",
"SkipDescription": "Nobena datoteka ne bo kopirana. Izvirna datoteka bo shranjena v ciljni mapi.",
"SkipTitle": "Preskoči"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} isimli dosya {{folder}} klasöründe zaten mevcut.",
"ConflictResolveDescriptionFiles": "{{filesCount}} belgeleri, '{{folder}}' klasöründe aynı isimle zaten mevcut.",
"ConflictResolveDescription": "<1>{{file}}</1> isimli dosya <1>{{folder}}</1> klasöründe zaten mevcut.",
"ConflictResolveDescriptionFiles": "{{filesCount}} belgeleri, <1>{{folder}}</1> klasöründe aynı isimle zaten mevcut.",
"ConflictResolveSelectAction": "Lütfen işlemi seçin:",
"ConflictResolveTitle": "Üstüne yazma onayı",
"CreateDescription": "Klasörde iki farklı dosya olacaktır.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sürüm güncellemesiyle üstüne yaz",
"SkipDescription": "Hiçbir dosya kopyalanmayacaktır. Orijinal dosya hedef klasörde tutulacaktır.",
"SkipTitle": "Atla"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл із іменем {{file}} уже існує в папці {{folder}}.",
"ConflictResolveDescriptionFiles": "Документи ({{filesCount}}) з таким ім'ям вже існують у папці {{folder}}.",
"ConflictResolveDescription": "Файл із іменем <1>{{file}}<1> уже існує в папці <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "Документи ({{filesCount}}) з таким ім'ям вже існують у папці <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Оберіть дію:",
"ConflictResolveTitle": "Підтвердження перезапису",
"CreateDescription": "У папці буде два різних файли.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Перезапис з оновленням версії",
"SkipDescription": "Жоден файл не буде скопійовано. Оригінальний файл буде збережено у папці призначення.",
"SkipTitle": "Пропустити"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": " Tệp có tên {{file}} đã tồn tại trong thư mục {{folder}}.",
"ConflictResolveDescriptionFiles": " {{filesCount}} tài liệu có cùng tên đã tồn tại trong thư mục '{{folder}}.",
"ConflictResolveDescription": " Tệp có tên <1>{{file}}</1> đã tồn tại trong thư mục <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": " {{filesCount}} tài liệu có cùng tên đã tồn tại trong thư mục <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Vui lòng chọn hành động:",
"ConflictResolveTitle": "Xác nhận ghi đè",
"CreateDescription": "Sẽ có hai tệp khác nhau trong thư mục.",
@ -9,4 +9,4 @@
"OverwriteTitle": " Ghi đè bằng cập nhật phiên bản",
"SkipDescription": "Không có tệp nào được sao chép. Tệp gốc sẽ được giữ lại trong thư mục đích.",
"SkipTitle": "Bỏ qua"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{folder}}文件夹中已有名称为{{file}}的文件。",
"ConflictResolveDescriptionFiles": "{{folder}}文件夹中已有{{filesCount}}个名称相同的文档。",
"ConflictResolveDescription": "<1>{{folder}}</1>文件夹中已有名称为<1>{{file}}</1>的文件。",
"ConflictResolveDescriptionFiles": "<1>{{folder}}</1>文件夹中已有{{filesCount}}个名称相同的文档。",
"ConflictResolveSelectAction": "请选择操作:",
"ConflictResolveTitle": "覆盖确认",
"CreateDescription": "文件夹中将有两种不同文件。",
@ -9,4 +9,4 @@
"OverwriteTitle": "使用版本更新覆盖",
"SkipDescription": "不会复制任何文件。原始文件将在目标文件夹中保留。",
"SkipTitle": "跳过"
}
}

View File

@ -89,15 +89,15 @@ const Panels = (props) => {
selectFileDialogVisible && (
<SelectFileDialog
key="select-file-dialog"
resetTreeFolders
//resetTreeFolders
onSelectFile={createMasterForm}
isPanelVisible={selectFileDialogVisible}
onClose={onClose}
foldersType="exceptPrivacyTrashFolders"
ByExtension
searchParam={".docx"}
headerName={t("Translations:CreateMasterFormFromFile")}
titleFilesList={t("SelectFile:SelectDOCXFormat")}
dialogName={t("Translations:CreateMasterFormFromFile")}
filesListTitle={t("SelectFile:SelectDOCXFormat")}
creationButtonPrimary
withSubfolders={false}
/>

View File

@ -26,12 +26,12 @@ const StyledTreeMenu = styled(TreeMenu)`
background: ${(props) => !props.dragging && "none !important"};
}
.rc-tree-node-selected {
/* .rc-tree-node-selected {
background: ${(props) =>
!props.isPanel
? props.theme.filesArticleBody.background
: props.theme.filesArticleBody.panelBackground} !important;
}
!props.isPanel
? props.theme.filesArticleBody.background
: props.theme.filesArticleBody.panelBackground} !important;
} */
.rc-tree-treenode-disabled > span:not(.rc-tree-switcher),
.rc-tree-treenode-disabled > a,
@ -453,7 +453,7 @@ class TreeFolders extends React.Component {
isLoading,
onSelect,
dragging,
expandedKeys,
expandedPanelKeys,
treeFolders,
data,
@ -475,7 +475,7 @@ class TreeFolders extends React.Component {
onSelect={onSelect}
selectedKeys={selectedKeys}
loadData={this.onLoadData}
expandedKeys={expandedPanelKeys ? expandedPanelKeys : expandedKeys}
expandedKeys={expandedPanelKeys}
onExpand={this.onExpand}
onDragOver={this.onDragOver}
onDragLeave={this.onDragLeave}
@ -484,6 +484,7 @@ class TreeFolders extends React.Component {
gapBetweenNodes="22"
gapBetweenNodesTablet="26"
isFullFillSelection={false}
childrenCount={expandedPanelKeys?.length}
>
{this.getItems(data || treeFolders)}
</StyledTreeMenu>
@ -515,7 +516,6 @@ export default inject(
myFolderId,
commonFolderId,
isPrivacyFolder,
expandedKeys,
setExpandedKeys,
setExpandedPanelKeys,
getSubfolders,
@ -532,7 +532,6 @@ export default inject(
commonId: commonFolderId,
isPrivacy: isPrivacyFolder,
draggableItems: dragging ? selection : null,
expandedKeys,
treeFolders,
isLoading,
selectedKeys: useDefaultSelectedKeys
@ -547,8 +546,4 @@ export default inject(
getSubfolders,
};
}
)(
withTranslation(["Home", "Common"])(
withLoader(observer(TreeFolders))(<Loaders.TreeFolders />)
)
);
)(withTranslation(["Home", "Common"])(observer(TreeFolders)));

View File

@ -1,79 +1,50 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import Scrollbar from "@appserver/components/scrollbar";
import TreeFolders from "./TreeFolders";
import { StyledSelectFolderPanel } from "../panels/StyledPanels";
import { StyledTree } from "../panels/SelectionPanel/StyledSelectionPanel";
const FolderTreeBody = ({
isLoadingData,
expandedKeys,
folderList,
folderTree,
onSelect,
withoutProvider,
certainFolders,
isAvailable,
filter,
selectedKeys,
heightContent,
displayType,
isHeaderChildren,
theme,
isDisableTree,
}) => {
const { t } = useTranslation(["SelectFolder", "Common"]);
return (
<>
{!isLoadingData ? (
isAvailable ? (
<StyledSelectFolderPanel
heightContent={heightContent}
displayType={displayType}
isHeaderChildren={isHeaderChildren}
>
<div className="select-folder-dialog_tree-folder">
<Scrollbar id="folder-tree-scroll-bar">
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={folderList}
filter={filter}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
selectedKeys={selectedKeys}
needUpdate={false}
/>
</Scrollbar>
</div>
</StyledSelectFolderPanel>
) : (
<StyledSelectFolderPanel
heightContent={heightContent}
isHeaderChildren={isHeaderChildren}
>
<div className="tree-folder-empty-list select-folder-dialog_tree-folder">
<Text as="span">{t("NotAvailableFolder")}</Text>
</div>
</StyledSelectFolderPanel>
)
) : (
<StyledSelectFolderPanel heightContent={heightContent}>
<div className="tree-folder-Loader" key="loader">
<Loader
type="oval"
size="16px"
style={{
display: "inline",
marginRight: "10px",
marginTop: "16px",
}}
/>
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
{isAvailable ? (
<StyledTree theme={theme}>
<div className="selection-panel_tree-folder">
<Scrollbar id="folder-tree-scroll-bar">
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={folderTree}
filter={filter}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
selectedKeys={selectedKeys}
disabled={isDisableTree}
needUpdate={false}
/>
</Scrollbar>
</div>
</StyledSelectFolderPanel>
</StyledTree>
) : (
<StyledTree>
<div className="selection-panel_empty-folder">
<Text as="span">{t("NotAvailableFolder")}</Text>
</div>
</StyledTree>
)}
</>
);
@ -88,8 +59,15 @@ export default inject(
({ filesStore, treeFoldersStore, selectedFolderStore }) => {
const { filter, isLoading } = filesStore;
const { expandedPanelKeys } = treeFoldersStore;
const expandedKeysProp = expandedPanelKeys
? expandedPanelKeys
: selectedFolderStore.pathParts;
const expandedKeys = expandedKeysProp?.map((item) => item.toString());
!expandedPanelKeys && expandedKeys?.pop();
return {
expandedKeys: expandedPanelKeys ? expandedPanelKeys : null,
expandedKeys: expandedKeys,
filter,
isLoading,
};

View File

@ -4,7 +4,7 @@ import ModalDialog from "@appserver/components/modal-dialog";
import RadioButtonGroup from "@appserver/components/radio-button-group";
import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import { withTranslation } from "react-i18next";
import { withTranslation, Trans } from "react-i18next";
import { inject, observer } from "mobx-react";
import { ConflictResolveType } from "@appserver/common/constants";
import toastr from "studio/toastr";
@ -12,12 +12,21 @@ import styled from "styled-components";
const StyledModalDialog = styled(ModalDialog)`
.conflict-resolve-dialog-text {
padding-bottom: 8px;
padding-bottom: 12px;
}
.conflict-resolve-dialog-text-description {
padding-bottom: 16px;
}
.conflict-resolve-radio-button {
label{
display: flex;
align-items: flex-start;
}
svg {
overflow: visible;
margin-right: 8px;
margin-top: 3px
}
}
@ -25,7 +34,20 @@ const StyledModalDialog = styled(ModalDialog)`
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
width: 90%;
width: 100%;
}
.button-dialog-accept {
margin-right: 8px;
}
.modal-dialog-aside-footer, .modal-dialog-modal-footer {
border-top: ${(props) => props.theme.button.border.baseDisabled};
margin-left: -16px;
margin-right: -16px;
padding-left: 16px;
padding-right: 16px;
padding-top: 16px;
}
}
`;
@ -43,6 +65,7 @@ const ConflictResolveDialog = (props) => {
setMoveToPanelVisible,
setCopyPanelVisible,
setThirdPartyMoveDialogVisible,
theme,
} = props;
const {
@ -127,8 +150,12 @@ const ConflictResolveDialog = (props) => {
{
label: (
<div>
<Text>{t("OverwriteTitle")}</Text>
<Text>{t("OverwriteDescription")}</Text>
<Text fontWeight={600} fontSize={"14px"}>
{t("OverwriteTitle")}
</Text>
<Text color={theme.text.disableColor} fontSize={"12px"}>
{t("OverwriteDescription")}
</Text>
</div>
),
value: "overwrite",
@ -136,8 +163,12 @@ const ConflictResolveDialog = (props) => {
{
label: (
<div>
<Text>{t("CreateTitle")}</Text>
<Text>{t("CreateDescription")}</Text>
<Text fontWeight={600} fontSize={"14px"}>
{t("CreateTitle")}
</Text>
<Text color={theme.text.disableColor} fontSize={"12px"}>
{t("CreateDescription")}
</Text>
</div>
),
@ -146,8 +177,12 @@ const ConflictResolveDialog = (props) => {
{
label: (
<div>
<Text>{t("SkipTitle")}</Text>
<Text>{t("SkipDescription")}</Text>
<Text fontWeight={600} fontSize={"14px"}>
{t("SkipTitle")}
</Text>
<Text color={theme.text.disableColor} fontSize={"12px"}>
{t("SkipDescription")}
</Text>
</div>
),
value: "skip",
@ -163,17 +198,36 @@ const ConflictResolveDialog = (props) => {
isLoading={!tReady}
visible={visible}
onClose={onCloseDialog}
displayType="aside"
theme={theme}
style={{ maxWidth: "520px" }}
>
<ModalDialog.Header>{t("ConflictResolveTitle")}</ModalDialog.Header>
<ModalDialog.Body>
<Text className="conflict-resolve-dialog-text">
{singleFile
? t("ConflictResolveDescription", { file, folder: folderTitle })
: t("ConflictResolveDescriptionFiles", {
filesCount,
folder: folderTitle,
})}
<Text className="conflict-resolve-dialog-text-description">
{singleFile ? (
<Trans
t={t}
file={filesCount}
folder={folderTitle}
i18nKey="ConflictResolveDescription"
ns="ConflictResolveDialog"
>
The file with the name <strong>{{ file }}</strong> already exists
in the folder
<strong>{{ folder: folderTitle }}</strong>.
</Trans>
) : (
<Trans
t={t}
filesCount={filesCount}
folder={folderTitle}
i18nKey="ConflictResolveDescriptionFiles"
ns="ConflictResolveDialog"
>
{{ filesCount }} documents with the same name already exist in the
folder <strong>{{ folder: folderTitle }}</strong>.
</Trans>
)}
</Text>
<Text className="conflict-resolve-dialog-text">
{t("ConflictResolveSelectAction")}
@ -194,9 +248,8 @@ const ConflictResolveDialog = (props) => {
className="button-dialog-accept"
key="OkButton"
label={t("Common:OKButton")}
size="small"
size="normalTouchscreen"
primary
scale
onClick={onAcceptType}
//isLoading={isLoading}
/>
@ -204,8 +257,7 @@ const ConflictResolveDialog = (props) => {
className="button-dialog"
key="CancelButton"
label={t("Common:CancelButton")}
size="small"
scale
size="normalTouchscreen"
onClick={onCloseDialog}
//isLoading={isLoading}
/>
@ -214,7 +266,7 @@ const ConflictResolveDialog = (props) => {
);
};
export default inject(({ dialogsStore, uploadDataStore, filesStore }) => {
export default inject(({ auth, dialogsStore, uploadDataStore, filesStore }) => {
const {
conflictResolveDialogVisible: visible,
setConflictResolveDialogVisible,
@ -227,8 +279,10 @@ export default inject(({ dialogsStore, uploadDataStore, filesStore }) => {
const { itemOperationToFolder } = uploadDataStore;
const { activeFiles, setActiveFiles } = filesStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme,
items,
visible,
conflictResolveDialogData,

View File

@ -1,19 +1,11 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { withRouter } from "react-router";
import ModalDialog from "@appserver/components/modal-dialog";
import { withTranslation } from "react-i18next";
import TreeFolders from "../../FolderTreeBody/TreeFolders";
import { inject, observer } from "mobx-react";
import toastr from "studio/toastr";
import Button from "@appserver/components/button";
import styled from "styled-components";
const StyledModalDialog = styled(ModalDialog)`
.modal-dialog-aside-footer {
width: 90%;
}
`;
import SelectFolderDialog from "../SelectFolderDialog";
let operationData, fileWithConflicts, timerId;
const OperationsPanelComponent = (props) => {
const {
t,
@ -37,18 +29,34 @@ const OperationsPanelComponent = (props) => {
checkFileConflicts,
setThirdPartyMoveDialogVisible,
parentFolderId,
conflictResolveDialogVisible,
clearActiveOperations,
} = props;
const zIndex = 310;
const deleteAfter = false; // TODO: get from settings
const expandedKeys = props.expandedKeys.map((item) => item.toString());
const [isLoading, setIsLoading] = useState(false);
const [selectedFolder, setSelectedFolder] = useState(null);
const [folderTitle, setFolderTitle] = useState(null);
const [providerKey, setProviderKey] = useState(null);
const [intermediateHidden, setIntermediateHidden] = useState(false);
useEffect(() => {
if (conflictResolveDialogVisible === false) {
intermediateHidden && setIntermediateHidden(false);
} else {
isLoading && setIsLoading(false);
}
}, [conflictResolveDialogVisible]);
useEffect(() => {
intermediateHidden &&
setConflictDialogData(fileWithConflicts, operationData);
}, [intermediateHidden]);
useEffect(() => {
return () => {
clearTimeout(timerId);
timerId = null;
};
});
const onClose = () => {
if (isCopy) {
setCopyPanelVisible(false);
@ -59,7 +67,7 @@ const OperationsPanelComponent = (props) => {
setExpandedPanelKeys(null);
};
const onSubmit = () => {
const onSubmit = (selectedFolder, folderTitle, providerKey) => {
if (currentFolderId === selectedFolder) {
return;
}
@ -76,12 +84,6 @@ const OperationsPanelComponent = (props) => {
}
};
const onSelect = (folder, treeNode) => {
setProviderKey(treeNode.node.props.providerKey);
setFolderTitle(treeNode.node.props.title);
setSelectedFolder(isNaN(+folder[0]) ? folder[0] : +folder[0]);
};
const startOperation = async (isCopy, destFolderId, folderTitle) => {
const isProviderFolder = selection.find((x) => !x.providerKey);
const items =
@ -111,7 +113,7 @@ const OperationsPanelComponent = (props) => {
if (!folderIds.length && !fileIds.length) return;
const operationData = {
operationData = {
destFolderId,
folderIds,
fileIds,
@ -124,69 +126,60 @@ const OperationsPanelComponent = (props) => {
},
};
setIsLoading(true);
checkFileConflicts(destFolderId, folderIds, fileIds).then(
async (conflicts) => {
if (!timerId)
timerId = setTimeout(() => {
setIsLoading(true);
}, 500);
checkFileConflicts(destFolderId, folderIds, fileIds)
.then(async (conflicts) => {
if (conflicts.length) {
setConflictDialogData(conflicts, operationData);
fileWithConflicts = conflicts;
setIntermediateHidden(true);
setIsLoading(false);
} else {
setIsLoading(false);
onClose();
await itemOperationToFolder(operationData);
}
}
);
})
.catch((e) => {
toastr.error(e);
setIsLoading(false);
clearActiveOperations(fileIds, folderIds);
})
.finally(() => {
clearTimeout(timerId);
timerId = null;
});
};
//console.log("Operations panel render");
// console.log("Operations panel render", expandedKeys);
const isVisible = intermediateHidden ? false : visible;
return (
<StyledModalDialog
visible={visible}
displayType="aside"
zIndex={zIndex}
<SelectFolderDialog
isDisableTree={isLoading}
foldersType="exceptSortedByTags"
isPanelVisible={isVisible}
onSubmit={onSubmit}
onClose={onClose}
isLoading={!tReady}
className="operations-panel-dialog"
>
<ModalDialog.Header>
{isRecycleBin
? t("Translations:Restore")
id={isRecycleBin ? null : currentFolderId}
withoutImmediatelyClose
dialogName={
isRecycleBin
? t("Common:Restore")
: isCopy
? t("Translations:Copy")
: t("Translations:Move")}
</ModalDialog.Header>
<ModalDialog.Body>
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={operationsFolders}
filter={filter}
onSelect={onSelect}
needUpdate={false}
disabled={isLoading || isLoading}
selectedKeys={[selectedFolder + ""]}
/>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
scale
key="OkButton"
label={
isRecycleBin
? t("Translations:Restore")
: isCopy
? t("Translations:Copy")
: t("Translations:Move")
}
size="small"
primary
onClick={onSubmit}
isLoading={isLoading}
isDisabled={!selectedFolder || isLoading}
/>
</ModalDialog.Footer>
</StyledModalDialog>
: t("Home:MoveTo")
}
buttonName={
isRecycleBin
? t("Common:RestoreHere")
: isCopy
? t("Translations:CopyHere")
: t("Translations:MoveHere")
}
></SelectFolderDialog>
);
};
@ -194,6 +187,7 @@ const OperationsPanel = withTranslation([
"OperationsPanel",
"Translations",
"Common",
"Home",
])(OperationsPanelComponent);
export default inject(
@ -213,10 +207,9 @@ export default inject(
isRecycleBinFolder,
operationsFolders,
setExpandedPanelKeys,
expandedPanelKeys,
} = treeFoldersStore;
const { setConflictDialogData, checkFileConflicts } = filesActionsStore;
const { itemOperationToFolder } = uploadDataStore;
const { itemOperationToFolder, clearActiveOperations } = uploadDataStore;
const {
moveToPanelVisible,
@ -227,6 +220,7 @@ export default inject(
setDestFolderId,
setThirdPartyMoveDialogVisible,
setIsFolderActions,
conflictResolveDialogVisible,
} = dialogsStore;
const selections = selection.length ? selection : [bufferSelection];
@ -237,9 +231,6 @@ export default inject(
const provider = selections.find((x) => x.providerKey);
return {
expandedKeys: expandedPanelKeys
? expandedPanelKeys
: selectedFolderStore.pathParts,
currentFolderId: selectedFolderStore.id,
parentFolderId: selectedFolderStore.parentId,
isRecycleBin: isRecycleBinFolder,
@ -259,6 +250,8 @@ export default inject(
setExpandedPanelKeys,
itemOperationToFolder,
checkFileConflicts,
conflictResolveDialogVisible,
clearActiveOperations,
};
}
)(withRouter(observer(OperationsPanel)));

View File

@ -1,170 +1,124 @@
import React, { useState } from "react";
import { StyledAsidePanel, StyledSelectFilePanel } from "../StyledPanels";
import React from "react";
import { StyledAsideBody } from "../SelectionPanel/StyledSelectionPanel";
import Text from "@appserver/components/text";
import SelectFolderInput from "../SelectFolderInput";
import FilesListBody from "./FilesListBody";
import Button from "@appserver/components/button";
import Loaders from "@appserver/common/components/Loaders";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
import ModalDialog from "@appserver/components/modal-dialog";
const DISPLAY_TYPE = "aside";
import FilesListWrapper from "../SelectionPanel/FilesListWrapper";
const SelectFileDialogAsideView = ({
t,
theme,
isPanelVisible,
zIndex,
onClose,
isVisible,
withoutProvider,
foldersType,
onSelectFile,
onClickInput,
onCloseSelectFolderDialog,
onSelectFolder,
filesList,
files,
hasNextPage,
isNextPageLoading,
loadNextPage,
selectedFolder,
titleFilesList,
loadingText,
selectedFile,
onClickSave,
onSetFileName,
fileName,
displayType,
isTranslationsReady,
passedId,
headerName,
isAvailableFolderList,
filesListTitle,
dialogName,
primaryButtonName,
theme,
onButtonClick,
folderId,
onSelectFolder,
resultingFolderTree,
footer,
fileId,
foldersType,
onCloseSelectFolderDialog,
onClickInput,
isFolderPanelVisible,
maxInputWidth,
newFilter,
}) => {
const [isLoadingData, setIsLoadingData] = useState(false);
const onSetLoadingData = (loading) => {
setIsLoadingData(loading);
};
const isHeaderChildren = !!titleFilesList;
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<StyledAsidePanel
visible={isPanelVisible}
onMouseUp={onMouseEvent}
onMouseDown={onMouseEvent}
>
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<ModalDialog
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
contentHeight="100%"
displayType={DISPLAY_TYPE}
contentPaddingBottom="0px"
displayType="aside"
withoutBodyScroll
>
<ModalDialog.Header>
{headerName ? headerName : t("SelectFile")}
</ModalDialog.Header>
<ModalDialog.Header>{dialogName}</ModalDialog.Header>
<ModalDialog.Body className="select-file_body-modal-dialog">
<StyledSelectFilePanel
theme={theme}
isHeaderChildren={isHeaderChildren}
displayType={DISPLAY_TYPE}
>
<div className="select-file-dialog_aside-body_wrapper">
<div className="select-file-dialog_aside-children"></div>
<div className="select-file-dialog_aside_body">
<StyledAsideBody theme={theme}>
<div className="selection-panel_aside-body">
<div className="selection-panel_folder-info">
<Text
className="selection-panel_folder-selection-title"
fontWeight={600}
>
{t("Translations:FolderSelection")}
</Text>
<SelectFolderInput
theme={theme}
onClickInput={onClickInput}
onClose={onCloseSelectFolderDialog}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
isPanelVisible={isVisible}
isPanelVisible={isFolderPanelVisible}
foldersType={foldersType}
isNeedArrowIcon
withoutProvider={withoutProvider}
isSetFolderImmediately
selectedFolderId={selectedFolder}
id={passedId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
dialogWithFiles
showButtons
selectionButtonPrimary
id={folderId}
onSelectFile={onSelectFile}
displayType="aside"
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
files={files}
folderTree={resultingFolderTree}
isFolderTreeLoading={!!!resultingFolderTree}
isNeedArrowIcon
maxInputWidth={maxInputWidth}
/>
{titleFilesList && (
<Text className="modal-dialog-filter-title">
{titleFilesList}
</Text>
)}
<div className="select-file-dialog_aside_body-files_list">
{selectedFolder && !isLoadingData ? (
<FilesListBody
theme={theme}
filesList={filesList}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
selectedFolder={selectedFolder}
displayType={DISPLAY_TYPE}
loadingText={loadingText}
selectedFile={selectedFile}
/>
) : isAvailableFolderList ? (
<div key="loader" className="panel-loader-wrapper">
<Loaders.Rows
theme={theme}
style={{
marginBottom: "24px",
marginTop: "20px",
}}
count={12}
/>
</div>
) : (
<div className="select-file-dialog_empty-container">
<EmptyContainer
theme={theme}
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
/>
</div>
)}
<Text color="#A3A9AE" className="selection-panel_aside-title">
{filesListTitle}
</Text>
</div>
<div className="selection-panel_files">
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType="aside"
folderSelection={false}
fileId={fileId}
newFilter={newFilter}
/>
</div>
<div className="selection-panel_aside-footer">
{footer}
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={!fileId}
/>
<Button
theme={theme}
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</div>
</div>
</StyledSelectFilePanel>
</StyledAsideBody>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFilePanel
theme={theme}
isHeaderChildren={isHeaderChildren}
>
<div className="select-file-dialog-aside_buttons">
<Button
theme={theme}
className="select-file-dialog-buttons-save"
primary
size="normal"
label={primaryButtonName}
onClick={onClickSave}
isDisabled={selectedFile.length === 0}
/>
<Button
size="normal"
theme={theme}
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</StyledSelectFilePanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
</div>
);
};
export default SelectFileDialogAsideView;

View File

@ -1,85 +0,0 @@
import React from "react";
import { StyledFilesList } from "../StyledPanels";
import { ReactSVG } from "react-svg";
import { inject, observer } from "mobx-react";
import Text from "@appserver/components/text";
import Checkbox from "@appserver/components/checkbox";
import RadioButton from "@appserver/components/radio-button";
const FilesListRow = ({
displayType,
needRowSelection,
index,
onSelectFile,
fileName,
children,
fileExst,
iconSrc,
isMultiSelect, // it will be needed
isChecked,
noCheckBox,
theme,
}) => {
return (
<StyledFilesList
displayType={displayType}
theme={theme}
needRowSelection={needRowSelection}
isChecked={isChecked}
noCheckBox={noCheckBox}
>
<div
data-index={index}
className="modal-dialog_file-name"
onClick={onSelectFile}
>
{isMultiSelect ? ( // it will be needed
<Checkbox
theme={theme}
label=""
isChecked={isChecked}
className="select-file-dialog_checked"
/>
) : (
<RadioButton
theme={theme}
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onSelectFile}
value=""
className="select-file-dialog_checked"
/>
)}
<ReactSVG src={iconSrc} className="select-file-dialog_icon" />
<div data-index={index} className="files-list_full-name">
<Text theme={theme} data-index={index} className="entry-title">
{fileName}
<Text
theme={theme}
data-index={index}
className="file-exst"
as="span"
>
{fileExst}
</Text>
</Text>
</div>
<div className="files-list_file-children_wrapper">{children}</div>
</div>
</StyledFilesList>
);
};
FilesListRow.defaultProps = {
isMultiSelect: false,
};
export default inject(({ settingsStore }, { fileExst }) => {
const iconSrc = settingsStore.getIconSrc(fileExst, 24);
return {
iconSrc,
};
})(observer(FilesListRow));

View File

@ -1,286 +0,0 @@
import React from "react";
import { StyledAsidePanel, StyledSelectFilePanel } from "../StyledPanels";
import ModalDialog from "@appserver/components/modal-dialog";
import SelectFolderDialog from "../SelectFolderDialog";
import FolderTreeBody from "../../FolderTreeBody";
import FilesListBody from "./FilesListBody";
import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import { isArrayEqual } from "@appserver/components/utils/array";
import { getFoldersTree } from "@appserver/common/api/files";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "../SelectFolderDialog/ExceptionFoldersConstants";
class SelectFileDialogModalView extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
isAvailable: true,
};
this.folderList = "";
this.noTreeSwitcher = false;
}
componentDidMount() {
const { onSetLoadingData } = this.props;
this.setState({ isLoadingData: true }, function () {
onSetLoadingData && onSetLoadingData(true);
this.trySwitch();
});
}
trySwitch = async () => {
const {
foldersType,
onSelectFolder,
selectedFolder,
passedId,
foldersList,
} = this.props;
switch (foldersType) {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
[
this.folderList,
this.noTreeSwitcher,
] = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
);
this.onSetSelectedFolder();
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "exceptPrivacyTrashFolders":
try {
const foldersTree = await getFoldersTree();
[
this.folderList,
this.noTreeSwitcher,
] = SelectFolderDialog.convertFolders(
foldersTree,
exceptPrivacyTrashFolders
);
this.onSetSelectedFolder();
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "common":
try {
this.folderList = await SelectFolderDialog.getCommonFolders();
!selectedFolder &&
onSelectFolder &&
onSelectFolder(
`${
selectedFolder
? selectedFolder
: passedId
? passedId
: this.folderList[0].id
}`
);
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "third-party":
try {
this.folderList = foldersList
? foldersList
: await SelectFolderDialog.getCommonThirdPartyList();
this.folderList.length !== 0
? this.onSetSelectedFolder()
: this.setState({ isAvailable: false });
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
}
};
loadersCompletes = () => {
const { onSetLoadingData } = this.props;
onSetLoadingData && onSetLoadingData(false);
this.setState({
isLoading: false,
});
};
onSetSelectedFolder = () => {
const { onSelectFolder, selectedFolder, passedId } = this.props;
onSelectFolder &&
onSelectFolder(
`${
selectedFolder
? selectedFolder
: passedId
? passedId
: this.folderList[0].id
}`
);
};
onSelect = (folder) => {
const { onSelectFolder, selectedFolder } = this.props;
if (isArrayEqual([folder[0]], [selectedFolder])) {
return;
}
onSelectFolder && onSelectFolder(folder[0]);
};
onMouseEvent = (event) => {
event.stopPropagation();
};
render() {
const {
t,
isPanelVisible,
onClose,
zIndex,
withoutProvider,
expandedKeys,
filter,
onSelectFile,
filesList,
hasNextPage,
isNextPageLoading,
loadNextPage,
selectedFolder,
titleFilesList,
loadingText,
selectedFile,
onClickSave,
headerName,
primaryButtonName,
theme,
} = this.props;
const { isLoading, isAvailable } = this.state;
const isHeaderChildren = !!titleFilesList;
return (
<StyledAsidePanel
theme={theme}
visible={isPanelVisible}
onMouseUp={this.onMouseEvent}
onMouseDown={this.onMouseEvent}
>
<ModalDialog
theme={theme}
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
className="select-file-modal-dialog"
style={{ maxWidth: "725px" }}
displayType="modal"
modalBodyPadding="0px"
isLoading={isLoading}
modalLoaderBodyHeight="277px"
>
<ModalDialog.Header theme={theme}>
{headerName ? headerName : t("SelectFile")}
</ModalDialog.Header>
<ModalDialog.Body
theme={theme}
className="select-file_body-modal-dialog"
>
<StyledSelectFilePanel
isHeaderChildren={isHeaderChildren}
theme={theme}
displayType="modal"
noTreeSwitcher={this.noTreeSwitcher}
>
<div className="modal-dialog_body">
<div className="modal-dialog_tree-body">
<FolderTreeBody
theme={theme}
expandedKeys={expandedKeys}
folderList={this.folderList}
onSelect={this.onSelect}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
filter={filter}
selectedKeys={[selectedFolder]}
isHeaderChildren={isHeaderChildren}
displayType="modal"
/>
</div>
<div className="modal-dialog_files-body">
<>
{titleFilesList && (
<Text theme={theme} className="modal-dialog-filter-title">
{titleFilesList}
</Text>
)}
{selectedFolder && (
<FilesListBody
theme={theme}
filesList={filesList}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
selectedFolder={selectedFolder}
loadingText={loadingText}
selectedFile={selectedFile}
listHeight={isHeaderChildren ? 260 : 303}
onSetLoadingData={this.onSetLoadingData}
displayType={"modal"}
/>
)}
</>
</div>
</div>
</StyledSelectFilePanel>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFilePanel
theme={theme}
isHeaderChildren={isHeaderChildren}
>
<div className="select-file-dialog-modal_buttons">
<Button
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
size="small"
label={primaryButtonName}
onClick={onClickSave}
isDisabled={selectedFile.length === 0}
/>
<Button
theme={theme}
className="modal-dialog-button"
size="small"
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</StyledSelectFilePanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
}
}
export default SelectFileDialogModalView;

View File

@ -0,0 +1,44 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import SelectFileDialog from "./index";
import stores from "../../../store/index";
import store from "studio/store";
import i18n from "./i18n";
const { auth: authStore } = store;
class SelectFileDialogBody extends React.Component {
componentDidMount() {
const { settings, setFilesSettings } = this.props;
settings && setFilesSettings(settings);
}
render() {
return <SelectFileDialog {...this.props} />;
}
}
const SelectFileWrapper = inject(({ settingsStore }) => {
const { setFilesSettings } = settingsStore;
return {
setFilesSettings,
};
})(observer(SelectFileDialogBody));
class SelectFileDialogWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileDialogWrapper;

View File

@ -1,59 +1,28 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import stores from "../../../store/index";
import i18n from "./i18n";
import SelectFileDialogModalView from "./ModalView";
import SelectFileDialogAsideView from "./AsideView";
import utils from "@appserver/components/utils";
//import SelectFolderDialog from "../SelectFolderDialog";
import { getFolder } from "@appserver/common/api/files";
import { FilterType } from "@appserver/common/constants";
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
import toastr from "studio/toastr";
const { desktop } = utils.device;
import store from "studio/store";
const { auth: authStore } = store;
class SelectFileDialogBody extends React.Component {
class SelectFileDialog extends React.Component {
constructor(props) {
super(props);
const {
folderId,
storeFolderId,
fileInfo,
filter,
creationButtonPrimary,
t,
} = props;
this.buttonName = creationButtonPrimary
? t("Common:Create")
: t("Common:SaveButton");
const { filter } = props;
this.state = {
isVisible: false,
selectedFolder: storeFolderId || "",
passedId: folderId,
selectedFile: fileInfo || "",
fileName: (fileInfo && fileInfo.title) || "",
filesList: [],
hasNextPage: true,
isNextPageLoading: false,
files: [],
displayType: this.getDisplayType(),
page: 0,
filterParams: this.getFilterParameters(),
isAvailableFolderList: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.newFilter = filter.clone();
this._isLoadNextPage = false;
}
getFilterParameters = () => {
@ -101,36 +70,79 @@ class SelectFileDialogBody extends React.Component {
};
setFilter = () => {
const { filterParams } = this.state;
const { withSubfolders = true } = this.props;
const filterParams = this.getFilterParameters();
this.newFilter.filterType = filterParams.filterType;
this.newFilter.search = filterParams.filterValue;
this.newFilter.withSubfolders = withSubfolders;
};
componentDidMount() {
authStore.init(true); // it will work if authStore is not initialized
async componentDidMount() {
const {
treeFolders,
foldersType,
id,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
treeFromInput,
displayType,
setFolderId,
folderId,
} = this.props;
!displayType && window.addEventListener("resize", this.throttledResize);
window.addEventListener("resize", this.throttledResize);
this.setFilter();
let resultingFolderTree, resultingId;
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
folderId,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
true
);
} catch (e) {
toastr.error(e);
return;
}
const tree = treeFromInput ? treeFromInput : resultingFolderTree;
if (tree.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
return;
}
setFolderId(resultingId);
this.setState({
resultingFolderTree: tree,
});
}
componentWillUnmount() {
const {
resetTreeFolders,
setExpandedPanelKeys,
setDefaultSelectedFolder,
setSelectedFolder,
setFolderId,
setFile,
setExpandedPanelKeys,
withoutResetFolderTree,
} = this.props;
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
if (resetTreeFolders) {
if (!withoutResetFolderTree) {
setExpandedPanelKeys(null);
//setSelectedFolder(null);
setFolderId(null);
setFile(null);
}
@ -161,192 +173,123 @@ class SelectFileDialogBody extends React.Component {
});
};
onSelectFolder = (id) => {
const { setFolderId } = this.props;
onSelectFolder = (folder) => {
const { displayType } = this.state;
const { setFolderId, setFile, folderId } = this.props;
const id = displayType === "aside" ? folder : folder[0];
if (id) {
if (id !== folderId) {
setFolderId(id);
this.setState({
selectedFolder: id,
hasNextPage: true,
filesList: [],
page: 0,
});
} else
this.setState({
isAvailableFolderList: false,
});
setFile(null);
}
};
onSelectFile = (e) => {
const { filesList } = this.state;
onSelectFile = (item, index) => {
const { setFile } = this.props;
const index = e.target.dataset.index || e.target.name;
if (!index) return;
setFile(filesList[+index]);
this.setState({
selectedFile: filesList[+index],
fileName: filesList[+index].title,
});
setFile(item);
};
onClickSave = () => {
const { onSetFileName, onClose, onSelectFile } = this.props;
const { fileName, selectedFile } = this.state;
const {
onSetFileNameAndLocation,
onClose,
onSelectFile,
fileInfo,
folderId,
} = this.props;
onSetFileName && onSetFileName(fileName);
onSelectFile && onSelectFile(selectedFile);
const fileName = fileInfo.title;
onSetFileNameAndLocation && onSetFileNameAndLocation(fileName, folderId);
onSelectFile && onSelectFile(fileInfo);
onClose && onClose();
};
loadNextPage = () => {
// const {
// setSelectedNode,
// setSelectedFolder,
// setExpandedPanelKeys,
// } = this.props;
const { selectedFolder, page } = this.state;
if (this._isLoadNextPage) return;
this._isLoadNextPage = true;
const pageCount = 30;
this.newFilter.page = page;
this.newFilter.pageCount = pageCount;
this.setState({ isNextPageLoading: true }, () => {
getFolder(selectedFolder, this.newFilter)
.then((data) => {
let newFilesList = page
? this.state.filesList.concat(data.files)
: data.files;
//TODO: it will need if passed the folder id - need to come up with a different solution.
// const newPathParts = SelectFolderDialog.convertPathParts(
//
// data.pathParts
// );
//setExpandedPanelKeys(newPathParts);
// setSelectedNode([selectedFolder + ""]);
// setSelectedFolder({
// folders: data.folders,
// ...data.current,
// pathParts: newPathParts,
// ...{ new: data.new },
// });
this.setState({
hasNextPage: newFilesList.length < data.total,
isNextPageLoading: false,
filesList: newFilesList,
page: page + 1,
});
})
.catch((error) => console.log(error))
.finally(() => (this._isLoadNextPage = false));
});
};
render() {
const {
t,
isPanelVisible,
onClose,
zIndex,
foldersType,
withoutProvider,
titleFilesList,
loadingLabel,
folderId,
onSetFileName,
tReady,
headerName,
foldersList,
filesListTitle,
theme,
header,
footer,
dialogName,
creationButtonPrimary,
maxInputWidth,
folderId,
fileInfo,
} = this.props;
const {
isVisible,
filesList,
hasNextPage,
isNextPageLoading,
selectedFolder,
displayType,
selectedFile,
fileName,
passedId,
isAvailableFolderList,
resultingFolderTree,
isLoadingData,
} = this.state;
const loadingText = loadingLabel
? loadingLabel
: `${t("Common:LoadingProcessing")} ${t("Common:LoadingDescription")}`;
const buttonName = creationButtonPrimary
? t("Common:Create")
: t("Common:SaveButton");
const name = dialogName ? dialogName : t("Common:SelectFile");
// console.log("Render file-component");
return displayType === "aside" ? (
<SelectFileDialogAsideView
theme={theme}
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
isFolderPanelVisible={isVisible}
onClose={onClose}
isVisible={isVisible}
withoutProvider={withoutProvider}
foldersType={foldersType}
filesList={filesList}
onSelectFile={this.onSelectFile}
onClickInput={this.onClickInput}
onClickSave={this.onClickSave}
onCloseSelectFolderDialog={this.onCloseSelectFolderDialog}
onSelectFolder={this.onSelectFolder}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
isTranslationsReady={tReady}
passedId={passedId}
titleFilesList={titleFilesList}
isAvailableFolderList={isAvailableFolderList}
primaryButtonName={this.buttonName}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onClickSave}
header={header}
dialogName={name}
footer={footer}
isLoadingData={isLoadingData}
primaryButtonName={buttonName}
isAvailable={isAvailableFolderList}
onSelectFolder={this.onSelectFolder}
onSelectFile={this.onSelectFile}
filesListTitle={filesListTitle}
fileId={fileInfo?.id}
newFilter={this.newFilter}
foldersType={foldersType}
onClickInput={this.onClickInput}
onCloseSelectFolderDialog={this.onCloseSelectFolderDialog}
maxInputWidth={maxInputWidth}
/>
) : (
<SelectFileDialogModalView
theme={theme}
<SelectionPanel
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
onClose={onClose}
withoutProvider={withoutProvider}
folderId={folderId}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onClickSave}
header={header}
dialogName={name}
footer={footer}
isLoadingData={isLoadingData}
primaryButtonName={buttonName}
isAvailable={isAvailableFolderList}
onSelectFolder={this.onSelectFolder}
onSelectFile={this.onSelectFile}
foldersType={foldersType}
onClickSave={this.onClickSave}
filesList={filesList}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
withoutProvider={withoutProvider}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
passedId={passedId}
titleFilesList={titleFilesList}
primaryButtonName={this.buttonName}
foldersList={foldersList}
filesListTitle={filesListTitle}
fileId={fileInfo?.id}
newFilter={this.newFilter}
/>
);
}
}
SelectFileDialogBody.propTypes = {
SelectFileDialog.propTypes = {
onClose: PropTypes.func.isRequired,
isPanelVisible: PropTypes.bool.isRequired,
onSelectFile: PropTypes.func.isRequired,
@ -356,67 +299,59 @@ SelectFileDialogBody.propTypes = {
"exceptSortedByTags",
"exceptPrivacyTrashFolders",
]),
folderId: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
withoutProvider: PropTypes.bool,
creationButtonPrimary: PropTypes.bool,
ignoreSelectedFolderTree: PropTypes.bool,
headerName: PropTypes.string,
titleFilesList: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
zIndex: PropTypes.number,
filesListTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
withoutResetFolderTree: PropTypes.bool,
};
SelectFileDialogBody.defaultProps = {
folderId: "",
titleFilesList: "",
SelectFileDialog.defaultProps = {
id: "",
filesListTitle: "",
withoutProvider: false,
zIndex: 310,
creationButtonPrimary: false,
ignoreSelectedFolderTree: false,
withoutResetFolderTree: false,
};
const SelectFileDialogWrapper = inject(
export default inject(
({
auth,
filesStore,
selectedFilesStore,
treeFoldersStore,
selectedFolderStore,
selectFileDialogStore,
}) => {
const {
folderId: storeFolderId,
folderId: id,
fileInfo,
setFolderId,
setFile,
} = selectedFilesStore;
} = selectFileDialogStore;
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { treeFolders, setExpandedPanelKeys } = treeFoldersStore;
const { filter } = filesStore;
const { setSelectedFolder, id } = selectedFolderStore;
const { id: storeFolderId } = selectedFolderStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
const folderId = id ? id : storeFolderId;
return {
storeFolderId: storeFolderId || id,
fileInfo,
setFile,
setFolderId,
setSelectedFolder,
setSelectedNode,
filter,
treeFolders,
storeFolderId,
folderId,
theme: theme,
setExpandedPanelKeys,
};
}
)(
observer(
withTranslation(["SelectFile", "Common", "Translations"])(
SelectFileDialogBody
)
withTranslation(["SelectFile", "Common", "Translations"])(SelectFileDialog)
)
);
class SelectFileDialog extends React.Component {
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileDialog;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFileInput from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFileModalWrapper = (props) => <SelectFileInput {...props} />;
class SelectFileInputWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileInputWrapper;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "currentOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFile"],
defaultNS: "SelectFile",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -1,7 +1,6 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFileDialog from "../SelectFileDialog";
import StyledComponent from "./StyledSelectFileInput";
@ -13,6 +12,7 @@ class SelectFileInputBody extends React.PureComponent {
this.state = {
fileName: "",
folderId: "",
};
}
@ -20,76 +20,46 @@ class SelectFileInputBody extends React.PureComponent {
this.props.setFirstLoad(false);
}
onSetFileName = (fileName) => {
onSetFileNameAndLocation = (fileName, id) => {
this.setState({
fileName: fileName,
folderId: id,
});
};
render() {
const {
name,
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isDisabled,
foldersType,
withSubfolders,
onSelectFile,
folderId,
headerName,
isImageOnly,
isArchiveOnly,
isDocumentsOnly,
searchParam,
isPresentationOnly,
isTablesOnly,
isMediaOnly,
loadingLabel,
titleFilesList,
zIndex,
fontSizeInput,
hasError,
t,
placeholder,
maxInputWidth,
foldersList,
maxFolderInputWidth,
isPanelVisible,
isDisabled,
isError,
...rest
} = this.props;
const { fileName } = this.state;
const { fileName, folderId } = this.state;
return (
<StyledComponent maxInputWidth={maxInputWidth}>
<SimpleFileInput
name={name}
className="select-file_file-input"
textField={fileName}
isDisabled={isDisabled}
isError={isError}
onClickInput={onClickInput}
fontSizeInput={fontSizeInput}
maxInputWidth={maxInputWidth}
/>
{isPanelVisible && (
<SelectFileDialog
zIndex={zIndex}
onClose={onClose}
{...rest}
id={folderId}
isPanelVisible={isPanelVisible}
foldersType={foldersType}
onSetFileName={this.onSetFileName}
withoutProvider={withoutProvider}
withSubfolders={withSubfolders}
onSelectFile={onSelectFile}
folderId={folderId}
headerName={headerName}
searchParam={searchParam}
isImageOnly={isImageOnly}
isArchiveOnly={isArchiveOnly}
isDocumentsOnly={isDocumentsOnly}
isPresentation={isPresentationOnly}
isTables={isTablesOnly}
isMediaOnly={isMediaOnly}
loadingLabel={loadingLabel}
titleFilesList={titleFilesList}
foldersList={foldersList}
onSetFileNameAndLocation={this.onSetFileNameAndLocation}
maxInputWidth={maxFolderInputWidth}
/>
)}
</StyledComponent>
@ -99,13 +69,13 @@ class SelectFileInputBody extends React.PureComponent {
SelectFileInputBody.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
hasError: PropTypes.bool,
placeholder: PropTypes.string,
};
SelectFileInputBody.defaultProps = {
withoutProvider: false,
isDisabled: false,
zIndex: 310,
hasError: false,
placeholder: "",
};
const SelectFileInputBodyWrapper = inject(({ filesStore }) => {

View File

@ -1,118 +1,122 @@
import React from "react";
import IconButton from "@appserver/components/icon-button";
import FolderTreeBody from "../../FolderTreeBody";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import Button from "@appserver/components/button";
import ModalDialog from "@appserver/components/modal-dialog";
import {
StyledAsideBody,
StyledAsideHeader,
} from "../SelectionPanel/StyledSelectionPanel";
import Text from "@appserver/components/text";
import Loaders from "@appserver/common/components/Loaders";
import styled, { css } from "styled-components";
const DISPLAY_TYPE = "aside";
const StyledModalDialog = styled(ModalDialog)`
.modal-dialog-aside-body {
padding-top: 12px;
}
`;
const SelectFolderDialogAsideView = ({
theme,
t,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
asideHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
resultingFolderTree,
onSelectFolder,
footer,
showButtons,
onSave,
headerName,
onButtonClick,
dialogName,
header,
canCreate,
isLoading,
primaryButtonName,
noTreeSwitcher,
isDisableTree,
isDisableButton,
}) => {
return (
<StyledAsidePanel theme={theme} visible={isPanelVisible}>
<ModalDialog
theme={theme}
visible={isPanelVisible}
zIndex={zIndex}
contentHeight="100%"
contentPaddingBottom={footer && showButtons ? "100px" : "40px"}
onClose={onClose}
withoutBodyScroll
displayType="aside"
>
<ModalDialog.Header theme={theme}>
<StyledSelectFolderPanel theme={theme}>
<div className="select-folder-dialog_header">
{isNeedArrowIcon && (
<IconButton
theme={theme}
className="select-folder-dialog_header-icon"
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={onClose}
// color={theme.filesPanels.selectFolder.color}
/>
)}
{headerName ? headerName : t("Translations:FolderSelection")}
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
contentHeight="100%"
contentPaddingBottom="0px"
onClose={onClose}
withoutBodyScroll
displayType="aside"
>
<ModalDialog.Header theme={theme}>
<StyledAsideHeader>
{isNeedArrowIcon && (
<IconButton
theme={theme}
className="selection-panel_aside-header-icon"
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={onClose}
/>
)}
{dialogName ? dialogName : t("Translations:FolderSelection")}
</StyledAsideHeader>
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledAsideBody theme={theme} header={!!header} footer={!!footer}>
<div className="selection-panel_aside-body">
<div className="selection-panel_aside-header">
<div>{header}</div>
<Text fontWeight="700" fontSize="18px">
{t("Translations:Documents")}
</Text>
</div>
</StyledSelectFolderPanel>
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledSelectFolderPanel
theme={theme}
displayType={DISPLAY_TYPE}
showButtons={showButtons}
isFooter={!!footer}
noTreeSwitcher={noTreeSwitcher}
>
<div className="select-folder-dialog_aside_body">
<div>{header} </div>
<FolderTreeBody
theme={theme}
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={asideHeightContent}
displayType={DISPLAY_TYPE}
/>
<div className="selection-panel_aside-tree">
{folderId && resultingFolderTree ? (
<FolderTreeBody
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
isDisableTree={isDisableTree}
displayType="aside"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFolderPanel theme={theme}>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<div className="selection-panel_aside-footer">
<div>{footer}</div>
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
className="select-folder-dialog-buttons-save"
primary
size="normal"
size="normalTouchscreen"
label={primaryButtonName}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
!isAvailable
}
/>
<Button
size="normal"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
isDisabled={isLoadingData}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
</div>
</div>
</StyledAsideBody>
</ModalDialog.Body>
</StyledModalDialog>
);
};
export default SelectFolderDialogAsideView;

View File

@ -1,95 +0,0 @@
import React from "react";
import ModalDialog from "@appserver/components/modal-dialog";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import FolderTreeBody from "../../FolderTreeBody";
import Button from "@appserver/components/button";
const SelectFolderDialogModalView = ({
t,
theme,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
header,
footer,
headerName,
showButtons,
onSave,
canCreate,
isLoading,
primaryButtonName,
noTreeSwitcher,
}) => {
return (
<StyledAsidePanel theme={theme} visible={isPanelVisible}>
<ModalDialog
theme={theme}
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
displayType="modal"
{...(!header && !footer && !showButtons && { contentHeight: "416px" })}
>
<ModalDialog.Header theme={theme}>
{headerName ? headerName : t("Translations:FolderSelection")}
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledSelectFolderPanel
theme={theme}
isNeedArrowIcon={isNeedArrowIcon}
noTreeSwitcher={noTreeSwitcher}
>
<div className="select-folder-modal-dialog-header">{header} </div>
<FolderTreeBody
theme={theme}
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={modalHeightContent}
/>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFolderPanel theme={theme}>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<Button
theme={theme}
className="select-folder-dialog-buttons-save"
primary
size="small"
label={primaryButtonName}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
/>
<Button
size="small"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
};
export default SelectFolderDialogModalView;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFolderDialog from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFolderModalWrapper = (props) => <SelectFolderDialog {...props} />;
class SelectFolderModal extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderModal;

View File

@ -24,7 +24,7 @@ newInstance.use(Backend).init({
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFolder", "Common", "Translations"],
ns: ["SelectFolder"],
defaultNS: "SelectFolder",
react: {
@ -32,4 +32,4 @@ newInstance.use(Backend).init({
},
});
export default newInstance;
export default newInstance;

View File

@ -1,377 +1,113 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import { getCommonThirdPartyList } from "@appserver/common/api/settings";
import {
getCommonFolderList,
getFolder,
getFolderPath,
getFoldersTree,
} from "@appserver/common/api/files";
import SelectFolderInput from "../SelectFolderInput";
import i18n from "./i18n";
import SelectFolderDialogAsideView from "./AsideView";
import SelectFolderDialogModalView from "./ModalView";
import stores from "../../../store/index";
import utils from "@appserver/components/utils";
import { FolderType } from "@appserver/common/constants";
import { isArrayEqual } from "@appserver/components/utils/array";
import store from "studio/store";
import toastr from "studio/toastr";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "./ExceptionFoldersConstants";
const { auth: authStore } = store;
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
import { FilterType } from "@appserver/common/constants";
const { desktop } = utils.device;
let pathName = "";
let folderList;
class SelectFolderModalDialog extends React.Component {
class SelectFolderDialog extends React.Component {
constructor(props) {
super(props);
const {
isSetFolderImmediately,
id,
displayType,
selectionButtonPrimary,
t,
} = this.props;
const isNeedFolder = id ? true : isSetFolderImmediately;
this.buttonName = selectionButtonPrimary
? t("Common:Select")
: t("Common:SaveButton");
const { id, displayType, filter } = this.props;
this.newFilter = filter.clone();
this.newFilter.filterType = FilterType.FilesOnly;
this.newFilter.withSubfolders = false;
this.state = {
isLoadingData: false,
isLoading: false,
isAvailable: true,
certainFolders: true,
folderId: "",
displayType: displayType || this.getDisplayType(),
isSetFolderImmediately: isNeedFolder,
canCreate: true,
isAvailable: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.folderTitle = "";
this.noTreeSwitcher = false;
}
componentDidMount() {
const { onSetLoadingData, onSetLoadingInput, displayType } = this.props;
authStore.init(true); // it will work if authStore is not initialized
async componentDidMount() {
const {
treeFolders,
foldersType,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
displayType,
isNeedArrowIcon = false,
folderTree,
setFolderId,
withInput,
id,
storeFolderId,
} = this.props;
!displayType && window.addEventListener("resize", this.throttledResize);
this.setState({ isLoadingData: true }, function () {
onSetLoadingData && onSetLoadingData(true);
onSetLoadingInput && onSetLoadingInput(true);
this.trySwitch();
const initialFolderId = withInput ? id : storeFolderId;
let resultingFolderTree, resultingId;
if (!withInput && !isNeedArrowIcon) {
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
initialFolderId,
onSetBaseFolderPath,
onSelectFolder,
foldersList
);
} catch (e) {
toastr.error(e);
return;
}
}
const tree =
isNeedArrowIcon || withInput ? folderTree : resultingFolderTree;
if (tree.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
return;
}
const resId = isNeedArrowIcon || withInput ? id : resultingId;
onSelectFolder && onSelectFolder(resId);
isNeedArrowIcon && onSetBaseFolderPath(resId);
setFolderId(resId);
this.setState({
resultingFolderTree: tree,
});
}
componentDidUpdate(prevProps) {
const {
storeFolderId,
canCreate,
showButtons,
selectionButtonPrimary,
isReset,
} = this.props;
if (
showButtons &&
!selectionButtonPrimary &&
storeFolderId !== prevProps.storeFolderId
) {
this.setState({
canCreate: canCreate,
isLoading: false,
});
}
const { isReset } = this.props;
if (isReset && isReset !== prevProps.isReset) {
this.onResetInfo();
}
}
trySwitch = async () => {
const {
folderPath,
onSelectFolder,
onSetBaseFolderPath,
foldersType,
id,
selectedFolderId,
foldersList,
} = this.props;
switch (foldersType) {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
);
this.setBaseSettings();
} catch (err) {
console.error("error", err);
this.loadersCompletes();
}
break;
case "exceptPrivacyTrashFolders":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptPrivacyTrashFolders
);
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "common":
try {
folderList = await SelectFolderDialog.getCommonFolders();
folderPath.length === 0 &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
!id &&
!selectedFolderId &&
onSetBaseFolderPath &&
onSetBaseFolderPath(folderList[0].title);
this.setFolderInfo();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "third-party":
try {
folderList = foldersList
? foldersList
: await SelectFolderDialog.getCommonThirdPartyList();
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
}
};
loadersCompletes = () => {
const {
onSetLoadingData,
onSetLoadingInput,
} = this.props;
onSetLoadingData && onSetLoadingData(false);
onSetLoadingInput && onSetLoadingInput(false);
this.setState({
isLoadingData: false,
});
};
setBaseSettings = async () => {
const { isSetFolderImmediately } = this.state;
const {
onSelectFolder,
onSetBaseFolderPath,
id,
selectedFolderId,
showButtons,
} = this.props;
if (folderList.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
this.loadersCompletes();
return;
}
!id && showButtons && this.setFolderToTree(folderList[0].id);
isSetFolderImmediately &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(
`${selectedFolderId ? selectedFolderId : id ? id : folderList[0].id}`
);
isSetFolderImmediately &&
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
if (onSetBaseFolderPath) {
try {
this.folderTitle = await SelectFolderDialog.getFolderPath(
id ? id : folderList[0].id
);
!id &&
!selectedFolderId &&
isSetFolderImmediately &&
onSetBaseFolderPath(this.folderTitle);
} catch (err) {
console.error(err);
}
}
this.setFolderInfo();
};
setFolderInfo = () => {
const {
id,
onSetFileName,
fileName,
selectedFolderId,
dialogWithFiles,
onSetBaseFolderPath,
} = this.props;
fileName && onSetFileName && onSetFileName(fileName);
if (!id && !selectedFolderId) {
this.loadersCompletes();
return;
}
if (selectedFolderId) {
onSetBaseFolderPath
? this.setBaseFolderPath(selectedFolderId)
: this.loadersCompletes();
}
if (id && !selectedFolderId) {
if (!dialogWithFiles) this.setSelectedFolder(id);
else {
this.setBaseFolderPath(id);
}
}
};
setBaseFolderPath = () => {
const { onSetBaseFolderPath, selectedFolderId } = this.props;
SelectFolderDialog.getFolderPath(selectedFolderId)
.then((folderPath) => (this.folderTitle = folderPath))
.then(() => onSetBaseFolderPath(this.folderTitle))
.catch((error) => console.log("error", error))
.finally(() => {
this.loadersCompletes();
});
};
setSelectedFolder = async (id) => {
const { onSetBaseFolderPath } = this.props;
let folder,
folderPath,
requests = [];
requests.push(getFolder(id));
if (onSetBaseFolderPath) {
requests.push(getFolderPath(id));
}
try {
[folder, folderPath] = await Promise.all(requests);
} catch (e) {
console.error(e);
}
folder && this.setFolderObjectToTree(id, folder);
if (onSetBaseFolderPath && folderPath) {
this.folderTitle = SelectFolderInput.setFullFolderPath(folderPath);
onSetBaseFolderPath(this.folderTitle);
}
this.loadersCompletes();
};
setFolderToTree = (id) => {
getFolder(id)
.then((data) => {
this.setFolderObjectToTree(id, data);
})
.catch((error) => console.log("error", error));
};
setFolderObjectToTree = (id, data) => {
const {
setSelectedNode,
setSelectedFolder,
selectionButtonPrimary,
setExpandedPanelKeys,
onSetBaseFolderPath,
} = this.props;
const isInput = !!onSetBaseFolderPath;
if (!selectionButtonPrimary || isInput) {
//TODO: it need for canCreate function now, will need when passed the folder id - need to come up with a different solution.
setSelectedNode([id + ""]);
const newPathParts = SelectFolderDialog.convertPathParts(data.pathParts);
isInput && setExpandedPanelKeys(newPathParts);
setSelectedFolder({
folders: data.folders,
...data.current,
pathParts: newPathParts,
...{ new: data.new },
});
}
};
componentWillUnmount() {
const {
setExpandedPanelKeys,
resetTreeFolders,
setSelectedFolder,
dialogWithFiles,
} = this.props;
const { setFolderTitle, setProviderKey, setFolderId } = this.props;
console.log("componentWillUnmount");
if (this.throttledResize) {
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
}
if (resetTreeFolders && !dialogWithFiles) {
setExpandedPanelKeys(null);
setSelectedFolder(null);
}
setFolderTitle("");
setProviderKey(null);
setFolderId(null);
}
getDisplayType = () => {
const displayType =
@ -386,132 +122,52 @@ class SelectFolderModalDialog extends React.Component {
this.setState({ displayType: displayType });
};
onSelect = async (folder) => {
const {
onSelectFolder,
onClose,
showButtons,
onSetFullPath,
selectionButtonPrimary,
onSetLoadingData,
onSetLoadingInput,
} = this.props;
const { folderId } = this.state;
onSelect = async (folder, treeNode) => {
const { setFolderId, folderId } = this.props;
let requests = [];
if (+folderId === +folder[0]) return;
if (isArrayEqual([folder[0]], [folderId])) {
return;
}
onSetLoadingData && onSetLoadingData(true);
onSetLoadingInput && onSetLoadingInput(true);
this.setState({
folderId: folder[0],
});
let folderInfo, folderPath;
if (showButtons && !selectionButtonPrimary) {
this.setState({
isLoading: true,
canCreate: false,
});
}
try {
if (showButtons && onSetFullPath) {
requests.push(getFolder(folder[0]), getFolderPath(folder));
[folderInfo, folderPath] = await Promise.all(requests);
} else {
showButtons
? (folderInfo = await getFolder(folder[0]))
: (folderPath = await getFolderPath(folder));
}
if (folderInfo) {
this.setFolderObjectToTree(folder[0], folderInfo);
}
if (folderPath) {
pathName = SelectFolderInput.setFullFolderPath(folderPath);
onSetFullPath && onSetFullPath(pathName);
}
} catch (e) {
console.error(e);
toastr.error();
if (showButtons) {
this.setState({
isLoading: false,
canCreate: true,
});
onClose && onClose();
}
}
onSelectFolder && onSelectFolder(folder[0]);
!showButtons && onClose && onClose();
this.loadersCompletes();
setFolderId(folder[0]);
};
onSave = (e) => {
const { onClose, onSave } = this.props;
const { folderId } = this.state;
onSave && onSave(e, folderId);
onClose = () => {
const { setExpandedPanelKeys, onClose, treeFolders } = this.props;
if (!treeFolders.length) {
setExpandedPanelKeys(null);
}
onClose && onClose();
};
onCloseAside = () => {
const { onClose } = this.props;
onClose && onClose();
};
onButtonClick = (e) => {
const {
onSave,
onSetNewFolderPath,
onSelectFolder,
withoutImmediatelyClose,
onSubmit,
providerKey,
folderTitle,
folderId,
} = this.props;
onSubmit && onSubmit(folderId, folderTitle, providerKey);
onSave && onSave(e, folderId);
onSetNewFolderPath && onSetNewFolderPath(folderId);
onSelectFolder && onSelectFolder(folderId);
!withoutImmediatelyClose && this.onClose();
};
onResetInfo = async () => {
const { id, foldersType, onSelectFolder } = this.props;
switch (foldersType) {
case "common":
try {
if (!id) {
folderList = await SelectFolderDialog.getCommonFolders();
}
onSelectFolder && onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${id ? id : folderList[0].id}`,
});
this.setFolderToTree(id ? id : folderList[0].id);
this.loadersCompletes();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "third-party":
try {
if (!id) {
folderList = await SelectFolderDialog.getCommonThirdPartyList();
}
onSelectFolder && onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${id ? id : folderList[0].id}`,
});
this.setFolderToTree(id ? id : folderList[0].id);
this.loadersCompletes();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
}
const { id, setFolderId } = this.props;
setFolderId(id);
};
render() {
@ -520,84 +176,88 @@ class SelectFolderModalDialog extends React.Component {
theme,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
asideHeightContent,
isNeedArrowIcon, //for aside view when selected file
header,
headerName,
dialogName,
footer,
showButtons,
buttonName,
isDisableTree,
folderId,
folderTitle,
expandedKeys,
isDisableButton,
} = this.props;
const {
isAvailable,
certainFolders,
folderId,
displayType,
isLoadingData,
canCreate,
isLoading,
isAvailable,
resultingFolderTree,
} = this.state;
const primaryButtonName = buttonName
? buttonName
: t("Common:SaveHereButton");
const name = dialogName ? dialogName : t("Common:SaveButton");
//console.log("Render Folder Component?", this.state);
return displayType === "aside" ? (
<SelectFolderDialogAsideView
theme={theme}
t={t}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
onClose={this.onCloseAside}
withoutProvider={withoutProvider}
isNeedArrowIcon={isNeedArrowIcon}
asideHeightContent={asideHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
certainFolders={true}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
resultingFolderTree={resultingFolderTree}
onSelectFolder={this.onSelect}
onButtonClick={this.onButtonClick}
header={header}
headerName={headerName}
dialogName={isNeedArrowIcon ? t("Translations:FolderSelection") : name}
footer={footer}
showButtons={showButtons}
isLoadingData={isLoadingData}
canCreate={canCreate}
isLoading={isLoading}
primaryButtonName={this.buttonName}
noTreeSwitcher={this.noTreeSwitcher}
primaryButtonName={
isNeedArrowIcon ? t("Common:Select") : primaryButtonName
}
isAvailable={isAvailable}
isDisableTree={isDisableTree}
isDisableButton={isDisableButton}
/>
) : (
<SelectFolderDialogModalView
<SelectionPanel
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
onClose={this.onClose}
withoutProvider={withoutProvider}
modalHeightContent={modalHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onButtonClick}
header={header}
headerName={headerName}
dialogName={name}
footer={footer}
showButtons={showButtons}
canCreate={canCreate}
isLoadingData={isLoadingData}
isLoading={isLoading}
primaryButtonName={this.buttonName}
noTreeSwitcher={this.noTreeSwitcher}
primaryButtonName={primaryButtonName}
isAvailable={isAvailable}
onSelectFolder={this.onSelect}
folderTitle={folderTitle}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
folderSelection
newFilter={this.newFilter}
isDisableButton={isDisableButton}
/>
);
}
}
SelectFolderModalDialog.propTypes = {
SelectFolderDialog.propTypes = {
onSelectFolder: PropTypes.func,
onClose: PropTypes.func.isRequired,
onClose: PropTypes.func,
isPanelVisible: PropTypes.bool.isRequired,
foldersType: PropTypes.oneOf([
"common",
@ -606,153 +266,60 @@ SelectFolderModalDialog.propTypes = {
"exceptPrivacyTrashFolders",
]),
displayType: PropTypes.oneOf(["aside", "modal"]),
id: PropTypes.string,
zIndex: PropTypes.number,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
withoutProvider: PropTypes.bool,
isNeedArrowIcon: PropTypes.bool,
dialogWithFiles: PropTypes.bool,
showButtons: PropTypes.bool,
selectionButtonPrimary: PropTypes.bool,
modalHeightContent: PropTypes.string,
asideHeightContent: PropTypes.string,
withoutImmediatelyClose: PropTypes.bool,
isDisableTree: PropTypes.bool,
};
SelectFolderModalDialog.defaultProps = {
isSetFolderImmediately: false,
dialogWithFiles: false,
isNeedArrowIcon: false,
SelectFolderDialog.defaultProps = {
id: "",
modalHeightContent: "291px",
asideHeightContent: "100%",
zIndex: 310,
withoutProvider: false,
folderPath: "",
showButtons: false,
selectionButtonPrimary: false,
withoutImmediatelyClose: false,
isDisableTree: false,
};
const SelectFolderDialogWrapper = inject(
export default inject(
({
treeFoldersStore,
selectedFolderStore,
selectedFilesStore,
selectFolderDialogStore,
filesStore,
auth,
}) => {
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { canCreate } = filesStore;
const { setSelectedFolder, id } = selectedFolderStore;
const { setFolderId, setFile } = selectedFilesStore;
const { treeFolders, setExpandedPanelKeys } = treeFoldersStore;
const { filter } = filesStore;
const { id } = selectedFolderStore;
const {
setFolderId,
setFolderTitle,
setProviderKey,
providerKey,
folderTitle,
folderId,
} = selectFolderDialogStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme: auth.settingsStore.theme,
setSelectedFolder,
setSelectedNode,
canCreate,
theme: theme,
storeFolderId: id,
providerKey,
folderTitle,
folderId,
setExpandedPanelKeys,
setFolderId,
setFile,
setFolderTitle,
setProviderKey,
treeFolders,
filter,
};
}
)(
observer(
withTranslation(["SelectFolder", "Common", "Translations"])(
SelectFolderModalDialog
SelectFolderDialog
)
)
);
class SelectFolderDialog extends React.Component {
static getCommonThirdPartyList = async () => {
const commonThirdPartyArray = await getCommonThirdPartyList();
commonThirdPartyArray.map((currentValue, index) => {
commonThirdPartyArray[index].key = `0-${index}`;
});
return commonThirdPartyArray;
};
static getCommonFolders = async () => {
const commonFolders = await getCommonFolderList();
const convertedData = {
id: commonFolders.current.id,
key: 0 - 1,
parentId: commonFolders.current.parentId,
title: commonFolders.current.title,
rootFolderType: +commonFolders.current.rootFolderType,
rootFolderName: "@common",
folders: commonFolders.folders.map((folder) => {
return {
id: folder.id,
title: folder.title,
access: folder.access,
foldersCount: folder.foldersCount,
rootFolderType: folder.rootFolderType,
providerKey: folder.providerKey,
newItems: folder.new,
};
}),
pathParts: commonFolders.pathParts,
foldersCount: commonFolders.current.foldersCount,
newItems: commonFolders.new,
};
return [convertedData];
};
static getFolderPath = async (folderId) => {
const foldersArray = await getFolderPath(folderId);
const convertFoldersArray = SelectFolderInput.setFullFolderPath(
foldersArray
);
return convertFoldersArray;
};
static convertPathParts = (pathParts) => {
let newPathParts = [];
for (let i = 0; i < pathParts.length - 1; i++) {
if (typeof pathParts[i] === "number") {
newPathParts.push(String(pathParts[i]));
} else {
newPathParts.push(pathParts[i]);
}
}
return newPathParts;
};
static convertFolders = (folders, arrayOfExceptions) => {
let newArray = [];
let noSubfoldersCount = 0;
let needHideSwitcher = false;
for (let i = 0; i < folders.length; i++) {
if (!arrayOfExceptions.includes(folders[i].rootFolderType)) {
newArray.push(folders[i]);
if (
folders[i].foldersCount === 0 ||
folders[i].rootFolderType === FolderType.Privacy
) {
noSubfoldersCount += 1;
}
}
}
if (newArray.length === noSubfoldersCount) {
needHideSwitcher = true;
}
return [newArray, needHideSwitcher];
};
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderDialog;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFolderInput from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFolderModalWrapper = (props) => <SelectFolderInput {...props} />;
class SelectFolderInputWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderInputWrapper;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "currentOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFolder"],
defaultNS: "SelectFolder",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -1,204 +1,229 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import { inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFolderDialog from "../SelectFolderDialog/index";
import StyledComponent from "./StyledSelectFolderInput";
import { getFolderPath } from "@appserver/common/api/files";
import toastr from "@appserver/components/toast/toastr";
import SelectFolderDialog from "../SelectFolderDialog";
import SimpleFileInput from "../../SimpleFileInput";
let path = "";
class SelectFolderInputBody extends React.PureComponent {
import { withTranslation } from "react-i18next";
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
class SelectFolderInput extends React.PureComponent {
constructor(props) {
super(props);
const { id, foldersType } = this.props;
const isNeedLoader =
!!id || foldersType !== "third-party" || foldersType === "common";
this.state = {
isLoading: false,
isLoading: isNeedLoader,
baseFolderPath: "",
fullFolderPath: "",
newFolderPath: "",
resultingFolderTree: [],
baseId: "",
};
}
componentDidMount() {
const { folderPath, setFirstLoad } = this.props;
if (folderPath.length !== 0) {
this.setState({
fullFolderPath: folderPath,
});
}
async componentDidMount() {
const {
setFirstLoad,
treeFolders,
foldersType,
id,
onSelectFolder,
foldersList,
} = this.props;
setFirstLoad(false);
let resultingFolderTree, resultingId;
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
id,
this.onSetBaseFolderPath,
onSelectFolder,
foldersList
);
} catch (e) {
toastr.error(e);
return;
}
this.setState({
resultingFolderTree,
baseId: resultingId,
});
}
componentDidUpdate(prevProps) {
const { isSuccessSave, isReset } = this.props;
const { fullFolderPath, baseFolderPath } = this.state;
const { isSuccessSave, isReset, setFolderId, id } = this.props;
const { newFolderPath, baseFolderPath, baseId } = this.state;
if (isSuccessSave && isSuccessSave !== prevProps.isSuccessSave) {
fullFolderPath &&
newFolderPath &&
this.setState({
baseFolderPath: fullFolderPath,
baseFolderPath: newFolderPath,
});
}
if (isReset && isReset !== prevProps.isReset) {
setFolderId(baseId !== id && id ? id : baseId);
this.setState({
fullFolderPath: baseFolderPath,
newFolderPath: baseFolderPath,
});
}
}
setFolderPath = async (folderId) => {
const foldersArray = await getFolderPath(folderId);
onSetFullPath = (pathName) => {
const convertFolderPath = (foldersArray) => {
let path = "";
if (foldersArray.length > 1) {
for (let item of foldersArray) {
if (!path) {
path = path + `${item.title}`;
} else path = path + " " + "/" + " " + `${item.title}`;
}
} else {
for (let item of foldersArray) {
path = `${item.title}`;
}
}
return path;
};
const convertFoldersArray = convertFolderPath(foldersArray);
return convertFoldersArray;
};
onSetNewFolderPath = async (folderId) => {
let timerId = setTimeout(() => {
this.setState({ isLoading: true });
}, 500);
try {
const convertFoldersArray = await this.setFolderPath(folderId);
clearTimeout(timerId);
timerId = null;
this.setState({
newFolderPath: convertFoldersArray,
isLoading: false,
});
} catch (e) {
toastr.error(e);
clearTimeout(timerId);
timerId = null;
this.setState({
isLoading: false,
});
}
};
onSetBaseFolderPath = async (folderId) => {
try {
const convertFoldersArray = await this.setFolderPath(folderId);
this.setState({
baseFolderPath: convertFoldersArray,
isLoading: false,
});
} catch (e) {
toastr.error(e);
this.setState({
isLoading: false,
});
}
};
onSetLoadingInput = (isLoading) => {
this.setState({
fullFolderPath: pathName,
isLoading,
});
};
onSetBaseFolderPath = (pathName) => {
this.setState({
baseFolderPath: pathName,
});
};
onSetLoadingInput = (loading) => {
this.setState({
isLoading: loading,
});
};
render() {
const {
name,
isLoading,
baseFolderPath,
newFolderPath,
baseId,
resultingFolderTree,
} = this.state;
const {
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isSavingProcess,
isDisabled,
onSelectFolder,
onSetLoadingData,
foldersType,
folderPath,
isNeedArrowIcon,
isSetFolderImmediately,
id,
selectedFolderId,
displayType,
dialogWithFiles,
modalHeightContent,
asideHeightContent,
zIndex,
showButtons,
header,
headerName,
footer,
selectionButtonPrimary,
fontSizeInput,
t,
placeholder,
maxInputWidth,
isReset,
foldersList,
isDisabled,
isPanelVisible,
id,
theme,
isFolderTreeLoading = false,
...rest
} = this.props;
const { isLoading, baseFolderPath, fullFolderPath } = this.state;
const passedId = baseId !== id && id ? id : baseId;
return (
<StyledComponent maxInputWidth={maxInputWidth} theme={theme}>
<StyledComponent maxWidth={maxInputWidth}>
<SimpleFileInput
theme={theme}
name={name}
className="select-folder_file-input"
textField={fullFolderPath || baseFolderPath}
isDisabled={isLoading || isSavingProcess || isDisabled}
textField={newFolderPath || baseFolderPath}
isError={isError}
onClickInput={onClickInput}
fontSizeInput={fontSizeInput}
maxInputWidth={maxInputWidth}
placeholder={placeholder}
isDisabled={isFolderTreeLoading || isDisabled || isLoading}
/>
<SelectFolderDialog
theme={theme}
zIndex={zIndex}
isPanelVisible={isPanelVisible}
onClose={onClose}
folderPath={folderPath}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
foldersType={foldersType}
withoutProvider={withoutProvider}
onSetFullPath={this.onSetFullPath}
onSetBaseFolderPath={this.onSetBaseFolderPath}
onSetLoadingInput={this.onSetLoadingInput}
isNeedArrowIcon={isNeedArrowIcon}
isSetFolderImmediately={isSetFolderImmediately}
id={id}
selectedFolderId={selectedFolderId}
displayType={displayType}
dialogWithFiles={dialogWithFiles}
modalHeightContent={modalHeightContent}
asideHeightContent={asideHeightContent}
showButtons={showButtons}
header={header}
headerName={headerName}
footer={footer}
selectionButtonPrimary={selectionButtonPrimary}
isReset={isReset}
foldersList={foldersList}
/>
{!isFolderTreeLoading && isPanelVisible && (
<SelectFolderDialog
{...rest}
withInput={true}
folderTree={resultingFolderTree}
id={passedId}
isPanelVisible={isPanelVisible}
onSetBaseFolderPath={this.onSetBaseFolderPath}
onSetNewFolderPath={this.onSetNewFolderPath}
/>
)}
</StyledComponent>
);
}
}
SelectFolderInputBody.propTypes = {
SelectFolderInput.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onSelectFolder: PropTypes.func.isRequired,
onSetLoadingData: PropTypes.func,
isPanelVisible: PropTypes.bool.isRequired,
name: PropTypes.string,
withoutProvider: PropTypes.bool,
isError: PropTypes.bool,
isSavingProcess: PropTypes.bool,
hasError: PropTypes.bool,
isDisabled: PropTypes.bool,
placeholder: PropTypes.string,
};
SelectFolderInputBody.defaultProps = {
withoutProvider: false,
SelectFolderInput.defaultProps = {
hasError: false,
isDisabled: false,
isError: false,
folderPath: "",
placeholder: "",
};
const SelectFolderInputBodyWrapper = inject(({ filesStore }) => {
const { setFirstLoad } = filesStore;
return {
setFirstLoad,
};
})(observer(SelectFolderInputBody));
class SelectFolderInput extends React.Component {
static setFullFolderPath = (foldersArray) => {
path = "";
if (foldersArray.length > 1) {
for (let item of foldersArray) {
if (!path) {
path = path + `${item.title}`;
} else path = path + " " + "/" + " " + `${item.title}`;
}
} else {
for (let item of foldersArray) {
path = `${item.title}`;
}
}
return path;
};
render() {
return (
<MobxProvider {...stores}>
<SelectFolderInputBodyWrapper {...this.props} />
</MobxProvider>
);
export default inject(
({ filesStore, treeFoldersStore, selectFolderDialogStore }) => {
const { setFirstLoad } = filesStore;
const { treeFolders } = treeFoldersStore;
const { setFolderId } = selectFolderDialogStore;
return {
setFirstLoad,
treeFolders,
setFolderId,
};
}
}
export default SelectFolderInput;
)(observer(withTranslation("Translations")(SelectFolderInput)));

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import { useTranslation, withTranslation } from "react-i18next";
@ -9,152 +9,163 @@ import { FixedSizeList as List } from "react-window";
import { inject, observer } from "mobx-react";
import FilesListRow from "./FilesListRow";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
import i18n from "./i18n";
import { StyledItemsLoader } from "../SelectionPanel/StyledSelectionPanel";
import Loaders from "@appserver/common/components/Loaders";
import { I18nextProvider } from "react-i18next";
let countLoad;
let countLoad, timerId;
const FilesListBody = ({
filesList,
files,
onSelectFile,
loadNextPage,
hasNextPage,
isNextPageLoading,
displayType,
viewer,
listHeight,
needRowSelection,
loadingText,
selectedFolder,
isMultiSelect,
selectedFile,
folderId,
fileId,
theme,
page,
folderSelection,
getIcon,
}) => {
const { t } = useTranslation(["SelectFile", "Common"]);
const [isLoading, setIsLoading] = useState(false);
const filesListRef = useRef(null);
if (page === 0) {
countLoad = 0;
}
useEffect(() => {
countLoad = 0;
return () => {
clearTimeout(timerId);
timerId = null;
};
}, []);
useEffect(() => {
if (filesListRef && filesListRef.current) {
filesListRef.current.resetloadMoreItemsCache(true);
}
}, [selectedFolder, displayType]);
}, [folderId, page, displayType]);
useEffect(() => {
if (isNextPageLoading) {
timerId = setTimeout(() => {
setIsLoading(true);
}, 500);
} else {
isLoading && setIsLoading(false);
clearTimeout(timerId);
timerId = null;
}
}, [isNextPageLoading]);
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? files.length + 1 : files.length;
// Every row is loaded except for our loading indicator row.
const isItemLoaded = useCallback(
(index) => {
return !hasNextPage || index < filesList.length;
const isLoaded = !hasNextPage || index < files.length;
if (isLoaded) {
clearTimeout(timerId);
timerId = null;
}
return isLoaded;
},
[filesList, hasNextPage]
[files, hasNextPage]
);
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? filesList.length + 1 : filesList.length;
const loadMoreItems = useCallback(() => {
if (folderId && page == 0 && isNextPageLoading) {
loadNextPage && loadNextPage();
return;
}
if (isNextPageLoading) return;
countLoad++;
loadNextPage && loadNextPage();
}, [isNextPageLoading, filesList, displayType]);
const renderPageLoader = useCallback(
(style) => {
return (
<div style={style}>
<div
key="loader"
className="panel-loader-wrapper loader-wrapper_margin"
>
<Loader
theme={theme}
type="oval"
size="16px"
className="panel-loader"
/>
<Text theme={theme} as="span">
{loadingText}
</Text>
</div>
</div>
);
},
[loadingText]
);
folderId && loadNextPage && loadNextPage();
}, [isNextPageLoading, files, displayType, folderId]);
const renderPageLoader = (style) => {
return (
<div style={style}>
<StyledItemsLoader key="loader">
<Loader
theme={theme}
type="oval"
size="16px"
className="panel-loader"
/>
<Text theme={theme} as="span">
{t("Common:LoadingProcessing")} {t("Common:LoadingDescription")}
</Text>
</StyledItemsLoader>
</div>
);
};
const renderFirstLoader = useCallback(
(style) => {
return (
<div style={style}>
<div
key="loader"
className="panel-loader-wrapper loader-wrapper_margin"
>
<Loaders.Rows
theme={theme}
style={{
marginBottom: displayType === "aside" ? "24px" : "26px",
marginTop: displayType === "aside" ? "8px" : "10px",
}}
count={displayType === "aside" ? 12 : 5}
/>
<div className="selection-panel_loader" key="loader">
{isLoading ? <Loaders.ListLoader withoutFirstRectangle /> : <></>}
</div>
</div>
);
},
[loadingText]
[isLoading]
);
const isFileChecked = useCallback(
(file) => {
const checked = selectedFile ? file.id === selectedFile.id : false;
(id) => {
const checked = fileId ? id === fileId : false;
return checked;
},
[selectedFile]
[fileId]
);
const Item = useCallback(
({ index, style }) => {
const isLoaded = isItemLoaded(index);
if (!isLoaded) {
if (countLoad >= 1) return renderPageLoader(style);
if (!isLoaded || !folderId) {
if (countLoad >= 1) {
return renderPageLoader(style);
}
return renderFirstLoader(style);
}
const file = filesList[index];
const fileName = file.title;
const fileExst = file.fileExst;
const modifyFileName = fileName.substring(
0,
fileName.indexOf(`${fileExst}`)
);
const fileOwner =
file.createdBy &&
((viewer.id === file.createdBy.id && t("Common:MeLabel")) ||
file.createdBy.displayName);
const isChecked = isFileChecked(file);
const item = files[index];
const isChecked = folderSelection ? false : isFileChecked(item.id);
return (
<div style={style}>
<FilesListRow
theme={theme}
displayType={displayType}
needRowSelection={needRowSelection}
index={index}
onSelectFile={onSelectFile}
fileName={modifyFileName}
fileExst={fileExst}
isMultiSelect={isMultiSelect}
item={item}
isChecked={isChecked}
>
<Text data-index={index} className="files-list_file-owner">
{fileOwner}
</Text>
</FilesListRow>
folderSelection={folderSelection}
icon={getIcon(
32,
item.fileExst,
item.providerKey,
item.contentLength
)}
/>
</div>
);
},
[filesList, selectedFile, displayType, renderFirstLoader, renderPageLoader]
[files, fileId, displayType, renderFirstLoader, renderPageLoader]
);
return (
<div className="files-list-body">
<div className="selection-panel_files-list-body">
<AutoSizer>
{({ width, height }) => (
<InfiniteLoader
@ -167,9 +178,9 @@ const FilesListBody = ({
{({ onItemsRendered, ref }) => (
<List
theme={theme}
height={displayType === "aside" ? height : listHeight}
height={height}
itemCount={itemCount}
itemSize={displayType === "aside" ? 56 : 50}
itemSize={48}
onItemsRendered={onItemsRendered}
ref={ref}
width={width + 8}
@ -187,7 +198,7 @@ const FilesListBody = ({
<EmptyContainer
theme={theme}
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
imageSrc="/static/images/empty.screen.react.svg"
/>
</div>
)}
@ -198,21 +209,12 @@ FilesListBody.defaultProps = {
listHeight: 300,
isMultiSelect: false,
};
const FilesListBodyWrapper = inject(({ auth }) => {
export default inject(({ auth, settingsStore }) => {
const { user } = auth.userStore;
const { getIcon } = settingsStore;
return {
viewer: user,
getIcon,
};
})(observer(withTranslation(["Common", "Home"])(FilesListBody)));
class FilesList extends React.Component {
render() {
return (
<I18nextProvider i18n={i18n}>
<FilesListBodyWrapper {...this.props} />
</I18nextProvider>
);
}
}
export default FilesList;

View File

@ -0,0 +1,54 @@
import React from "react";
import { StyledRow } from "./StyledSelectionPanel";
import Text from "@appserver/components/text";
import RadioButton from "@appserver/components/radio-button";
import ItemIcon from "../../ItemIcon";
const FilesListRow = ({
displayType,
index,
onSelectFile,
isChecked,
theme,
folderSelection,
icon,
item,
}) => {
const { id, fileExst, title } = item;
const element = <ItemIcon id={id} icon={icon} fileExst={fileExst} />;
const onFileClick = () => {
onSelectFile && onSelectFile(item, index);
};
return (
<StyledRow
displayType={displayType}
theme={theme}
isChecked={isChecked}
folderSelection={folderSelection}
>
<div className="selection-panel_icon">{element}</div>
<div className="selection-panel_text">
<Text fontSize="14px" fontWeight={600}>
{title}
</Text>
</div>
<div className="selection-panel_checkbox">
{!folderSelection && (
<RadioButton
//theme={theme}
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onFileClick}
value=""
className="select-file-dialog_checked"
/>
)}
</div>
</StyledRow>
);
};
export default FilesListRow;

View File

@ -0,0 +1,151 @@
import React from "react";
import { inject, observer } from "mobx-react";
import toastr from "studio/toastr";
import FilesListBody from "./FilesListBody";
import axios from "axios";
import { combineUrl, getFolderOptions } from "@appserver/common/utils";
import AppServerConfig from "@appserver/common/constants/AppServerConfig";
class FilesListWrapper extends React.Component {
constructor(props) {
super(props);
const { newFilter } = this.props;
this.newFilter = newFilter;
this.state = {
isNextPageLoading: false,
page: 0,
hasNextPage: true,
files: [],
};
}
componentDidUpdate(prevProps) {
const { folderId } = this.props;
const { isNextPageLoading } = this.state;
if (folderId !== prevProps.folderId) {
if (isNextPageLoading) {
this.source.cancel();
this._isLoadNextPage = false;
this.setState({
isNextPageLoading: false,
});
}
this.setState({
page: 0,
files: [],
hasNextPage: true,
});
}
}
_loadNextPage = () => {
const { files, page } = this.state;
const {
folderId,
setFolderTitle,
setProviderKey,
setFolderId,
folderSelection,
} = this.props;
if (this._isLoadNextPage) return;
const pageCount = 30;
this.newFilter.page = page;
this.newFilter.pageCount = pageCount;
this._isLoadNextPage = true;
this.setState({ isNextPageLoading: true }, async () => {
try {
this.CancelToken = axios.CancelToken;
this.source = this.CancelToken.source();
const options = getFolderOptions(folderId, this.newFilter);
const response = await axios
.get(combineUrl(AppServerConfig.apiPrefixURL, options.url), {
cancelToken: this.source.token,
})
.catch((thrown) => {
if (axios.isCancel(thrown)) {
console.log("Request canceled", thrown.message);
} else {
console.error(thrown);
}
return;
});
if (!response) return;
const data = response.data.response;
if (page === 0 && folderSelection) {
setFolderTitle(data.current.title);
setProviderKey(data.current.providerKey);
setFolderId(folderId);
}
const finalData = [...data.files];
const newFilesList = [...files].concat(finalData);
const hasNextPage = newFilesList.length < data.total - 1;
this._isLoadNextPage = false;
this.setState((state) => ({
hasNextPage: hasNextPage,
isNextPageLoading: false,
page: state.page + 1,
files: newFilesList,
}));
} catch (e) {
toastr.error(e);
}
});
};
render() {
const {
t,
theme,
onSelectFile,
folderSelection = false,
fileId,
folderId,
} = this.props;
const { hasNextPage, isNextPageLoading, files, page } = this.state;
return (
<FilesListBody
theme={theme}
files={files}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this._loadNextPage}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
fileId={fileId}
page={page}
/>
);
}
}
export default inject(
({ selectedFolderStore, selectFolderDialogStore, auth }) => {
const { id } = selectedFolderStore;
const {
setFolderId,
setFolderTitle,
setProviderKey,
} = selectFolderDialogStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme: theme,
storeFolderId: id,
setFolderId,
setFolderTitle,
setProviderKey,
};
}
)(observer(FilesListWrapper));

View File

@ -0,0 +1,278 @@
import React from "react";
import ModalDialog from "@appserver/components/modal-dialog";
import FolderTreeBody from "../../FolderTreeBody";
import Button from "@appserver/components/button";
import FilesListWrapper from "./FilesListWrapper";
import {
getCommonFoldersTree,
getFolder,
getFoldersTree,
getThirdPartyCommonFolderTree,
} from "@appserver/common/api/files";
import toastr from "studio/toastr";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "./ExceptionFoldersConstants";
import { StyledBody, StyledModalDialog } from "./StyledSelectionPanel";
import Text from "@appserver/components/text";
import Loaders from "@appserver/common/components/Loaders";
const SelectionPanelBody = ({
t,
isPanelVisible,
onClose,
withoutProvider,
onSelectFile,
filesListTitle,
dialogName,
primaryButtonName,
theme,
isLoading,
onButtonClick,
folderId,
onSelectFolder,
resultingFolderTree,
isAvailable,
footer,
header,
folderSelection = false,
folderTitle,
fileId,
canCreate = true,
isLoadingData,
expandedKeys,
isDisableTree,
page,
newFilter,
isDisableButton,
}) => {
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
onClose={onClose}
className="select-file-modal-dialog"
style={{ maxWidth: "773px" }}
displayType="modal"
modalBodyPadding="0px"
isLoading={isLoading}
>
<ModalDialog.Header theme={theme}>{dialogName}</ModalDialog.Header>
<ModalDialog.Body
theme={theme}
className="select-file_body-modal-dialog"
>
<StyledBody header={!!header} footer={!!footer}>
<div className="selection-panel_body">
<div className="selection-panel_tree-body">
<Text
fontWeight="700"
fontSize="18px"
className="selection-panel_folder-title"
>
{t("Translations:Documents")}
</Text>
{folderId && resultingFolderTree ? (
<FolderTreeBody
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
displayType="modal"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
<div className="selection-panel_files-body">
<>
<div className="selection-panel_files-header">
{header}
<Text
color="#A3A9AE"
theme={theme}
className="selection-panel_title"
>
{folderSelection
? t("FolderContents", { folderTitle })
: filesListTitle}
</Text>
</div>
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
newFilter={newFilter}
fileId={fileId}
/>
</>
</div>
<div className="selection-panel_footer">
<div>{footer}</div>
<div className="selection-panel_buttons">
<Button
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
(!fileId && !folderSelection) ||
!canCreate
}
isLoading={isDisableTree}
/>
<Button
theme={theme}
className="modal-dialog-button"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData}
/>
</div>
</div>
</div>
</StyledBody>
</ModalDialog.Body>
</StyledModalDialog>
</div>
);
};
class SelectionPanel extends React.Component {
static getFolderPath = async (id) => {
try {
const data = await getFolder(id);
const newPathParts = data.pathParts.map((item) => item.toString());
+newPathParts[newPathParts.length - 1] === +id && newPathParts.pop();
return newPathParts;
} catch (e) {
toastr.error(e);
return null;
}
};
static getBasicFolderInfo = async (
treeFolders,
foldersType,
id,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
isFilesPanel = false
) => {
const getRequestFolderTree = () => {
switch (foldersType) {
case "exceptSortedByTags":
case "exceptPrivacyTrashFolders":
try {
return getFoldersTree();
} catch (err) {
console.error(err);
}
break;
case "common":
try {
return getCommonFoldersTree();
} catch (err) {
console.error(err);
}
break;
case "third-party":
try {
return getThirdPartyCommonFolderTree();
} catch (err) {
console.error(err);
}
break;
}
};
const filterFoldersTree = (folders, arrayOfExceptions) => {
let newArray = [];
for (let i = 0; i < folders.length; i++) {
if (!arrayOfExceptions.includes(folders[i].rootFolderType)) {
newArray.push(folders[i]);
}
}
return newArray;
};
const getExceptionsFolders = (treeFolders) => {
switch (foldersType) {
case "exceptSortedByTags":
return filterFoldersTree(treeFolders, exceptSortedByTagsFolders);
case "exceptPrivacyTrashFolders":
return filterFoldersTree(treeFolders, exceptPrivacyTrashFolders);
}
};
let requestedTreeFolders, filteredTreeFolders;
const treeFoldersLength = treeFolders.length;
if (treeFoldersLength === 0) {
try {
requestedTreeFolders = foldersList
? foldersList
: await getRequestFolderTree();
} catch (e) {
toastr.error(e);
return;
}
}
const foldersTree =
treeFoldersLength > 0 ? treeFolders : requestedTreeFolders;
const passedId = id ? id : foldersTree[0].id;
if (foldersType === "third-party") {
isFilesPanel && onSetBaseFolderPath && onSetBaseFolderPath(passedId);
} else {
onSetBaseFolderPath && onSetBaseFolderPath(passedId);
}
onSelectFolder && onSelectFolder(passedId);
if (
foldersType === "exceptSortedByTags" ||
foldersType === "exceptPrivacyTrashFolders"
) {
filteredTreeFolders = getExceptionsFolders(foldersTree);
}
return [filteredTreeFolders || foldersTree, passedId];
};
render() {
return <SelectionPanelBody {...this.props} />;
}
}
export default SelectionPanel;

View File

@ -0,0 +1,284 @@
import styled, { css } from "styled-components";
import ModalDialog from "@appserver/components/modal-dialog";
const commonStyles = css`
.empty-folder_container {
grid-template-areas:
"img img"
"headerText headerText";
grid-template-rows: 72px 1fr;
padding-bottom: 0;
.ec-image {
margin: auto;
}
.ec-header {
margin: auto;
}
}
`;
const StyledModalDialog = styled(ModalDialog)`
.heading {
line-height: 52px;
font-size: 21px;
}
.modal-dialog-aside-header {
height: 53px;
}
`;
const StyledBody = styled.div`
.selection-panel_body {
height: 495px;
display: grid;
grid-template-columns: 245px 1fr;
grid-template-areas: "tree files" "footer footer";
grid-template-rows: auto max-content;
margin-right: -4px;
.selection-panel_files-body {
grid-area: files;
display: grid;
grid-template-rows: max-content auto;
}
.selection-panel_files-list-body {
height: 100%;
}
.selection-panel_tree-body {
grid-area: tree;
height: 100%;
border-right: 1px solid #eceef1;
display: grid;
grid-template-rows: max-content auto;
.selection-panel_folder-title {
padding: 12px 20px 14px 0px;
}
.selection-panel_tree-folder {
margin-left: -12px;
}
.span.rc-tree-switcher {
padding-left: 16px;
}
}
.selection-panel_files-header {
padding: 16px;
word-break: break-word;
.selection-panel_title {
${(props) => props.header && "padding-top: 16px"};
}
}
.selection-panel_footer {
grid-area: footer;
border-top: 1px solid ${(props) => props.theme.row.borderBottom};
margin-left: -13px;
margin-right: -7px;
padding-left: 16px;
padding-top: 16px;
.selection-panel_buttons {
${(props) => props.footer && "margin-top:16px"};
button:first-child {
margin-right: 8px;
}
}
}
}
${commonStyles}
`;
const StyledRow = styled.div`
display: grid;
grid-template-columns: 32px auto 32px;
grid-gap: 8px;
position: relative;
height: 48px;
width: calc(100% - 16px);
padding-left: 16px;
${(props) =>
props.isChecked && `background: ${props.theme.row.backgroundColor}`};
.selection-panel_clicked-area {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.selection-panel_text {
margin-top: auto;
margin-bottom: 16px;
overflow: hidden;
p {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.selection-panel_icon,
.selection-panel_checkbox {
margin: auto 0;
}
.selection-panel_icon {
svg {
path {
fill: #a3a9ae;
}
}
}
${(props) =>
props.folderSelection &&
css`
.selection-panel_icon {
::after {
position: absolute;
display: block;
background-color: #f3f4f4;
border-top-right-radius: 50%;
left: 18px;
top: 6px;
width: 27px;
height: 32px;
content: "";
opacity: 0.7;
}
}
.selection-panel_text p {
color: ${(props) => props.theme.text.disableColor};
}
`}
`;
const StyledAsideBody = styled.div`
height: 100%;
.selection-panel_aside-body {
height: calc(100% - 32px);
display: grid;
grid-template-rows: max-content auto max-content;
}
.selection-panel_files-list-body {
height: 100%;
margin-left: -16px;
margin-right: -6px;
}
.selection-panel_files {
height: 100%;
}
.selection-panel_aside-header {
div:first-child {
${(props) => props.header && " margin-bottom: 12px;"}
}
}
.selection-panel_aside-folder-title {
margin-top: 12px;
}
.selection-panel_folder-selection-title {
margin-bottom: 4px;
}
.selection-panel_aside-tree {
margin-top: 12px;
margin-left: -16px;
margin-right: -16px;
}
.selection-panel_aside-title {
padding-bottom: 16px;
}
.selection-panel_aside-footer {
border-top: 1px solid ${(props) => props.theme.row.borderBottom};
margin-left: -13px;
margin-right: -13px;
padding-left: 16px;
padding-right: 16px;
padding-top: 16px;
padding-bottom: 12px;
.selection-panel_aside-buttons {
${(props) => props.footer && "margin-top:16px"};
display: grid;
grid-template-columns: 1fr 1fr;
button:first-child {
margin-right: 8px;
}
}
}
${commonStyles}
`;
const StyledAsideHeader = styled.div`
display: flex;
align-items: center;
.selection-panel_aside-header-icon {
margin-right: 16px;
}
`;
const StyledTree = styled.div`
height: 100%;
.files-tree-menu {
margin-top: 0 !important;
margin-bottom: 22px;
}
.selection-panel_tree-folder {
height: 100%;
}
#folder-tree-scroll-bar {
.nav-thumb-horizontal {
height: 0px !important;
}
${(props) =>
props.displayType === "modal" &&
css`
.nav-thumb-vertical {
margin-left: 4px !important;
width: 4px !important;
}
`}
.scroll-body {
overflow-x: hidden !important;
}
}
.selection-panel_empty-folder {
margin-top: 12px;
margin-left: 12px;
}
`;
const StyledItemsLoader = styled.div`
display: flex;
.panel-loader {
margin-left: 16px;
margin-right: 8px;
}
`;
export {
StyledBody,
StyledRow,
StyledAsideBody,
StyledAsideHeader,
StyledTree,
StyledModalDialog,
StyledItemsLoader,
};

View File

@ -649,350 +649,6 @@ const StyledLinkRow = styled.div`
StyledLinkRow.defaultProps = { theme: Base };
const StyledSelectFolderPanel = styled.div`
${(props) =>
props.displayType === "aside" &&
css`
height: 100%;
`}
${(props) =>
props.noTreeSwitcher &&
css`
span.rc-tree-switcher.rc-tree-switcher-noop {
display: none;
}
`}
.modal-dialog_header {
display: flex;
align-items: center;
}
.select-folder-modal-dialog-header {
margin-bottom: 16px;
}
.modal-dialog_header-title {
${(props) => props.isNeedArrowIcon && `margin-left:16px;`}
}
.select-folder-dialog_tree-folder {
margin-top: 12px;
height: ${(props) => (props.displayType === "aside" ? "100%" : "291px")};
}
.select-folder-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-modal_buttons {
margin-top: 16px;
}
.select-folder-dialog_header {
display: flex;
align-items: center;
}
.select-folder-dialog_header-icon {
margin-right: 16px;
}
.select-folder-dialog_aside_body {
height: ${(props) =>
props.isFooter ? "calc(100% - 110px)" : "calc(100% - 64px)"};
width: 296px;
}
#folder-tree-scroll-bar {
.nav-thumb-horizontal {
height: 0px !important;
}
${(props) =>
props.displayType === "modal" &&
css`
.nav-thumb-vertical {
margin-left: 4px !important;
width: 4px !important;
}
`}
.scroll-body {
overflow-x: hidden !important;
}
}
.tree-folder-Loader {
${(props) =>
props.displayType === "aside"
? css`
margin-top: 16px;
`
: css`
height: ${props.heightContent};
`}
}
.files-tree-menu {
margin-top: 0 !important;
}
`;
const StyledSelectFilePanel = styled.div`
height: 100%;
${(props) =>
props.noTreeSwitcher &&
css`
span.rc-tree-switcher.rc-tree-switcher-noop {
display: none;
}
`}
.files-list-body {
height: 100%;
${(props) =>
props.displayType === "aside" &&
css`
margin-left: -16px;
margin-right: -16px;
.nav-thumb-vertical {
margin-left: -7px !important;
}
`}
${(props) =>
props.displayType === "modal" &&
css`
.nav-thumb-vertical {
margin-left: 4px !important;
width: 4px !important;
}
`}
}
.select-file-dialog_aside_body-files_list {
height: 100%;
}
.select-file-dialog_empty-container {
.ec-header {
word-break: break-word;
}
}
.modal-dialog-filter-title {
margin-top: 12px;
${(props) => props.displayType === "modal" && `margin-left: 12px`};
margin-bottom: 12px;
font-size: 12px;
line-height: 16px;
color: ${(props) => props.theme.filesPanels.selectFile.borderRight};
}
.select-file-dialog-modal_buttons {
${(props) =>
props.isHeaderChildren ? "margin-top: 20px" : "margin-top:20px"};
}
.select-file-dialog_aside-body_wrapper {
height: calc(100% - 109px);
}
.select-folder-dialog_aside-body_wrapper {
width: 320px;
box-sizing: border-box;
height: 100%;
}
.select-file-dialog_aside_body,
.select-file-dialog_aside_body_files-list {
height: 100%;
width: 293px;
}
.select-file-dialog_aside_body_files-list {
margin-left: -17px;
padding-left: 16px;
${(props) =>
props.isChecked &&
`background: ${(props) => props.theme.filesPanels.selectFile.background}`}
}
.file-name {
border-bottom: ${(props) =>
props.theme.filesPanels.selectFile.borderBottom};
}
.file-name {
display: flex;
padding: 7px 0px;
}
.panel-loader-wrapper {
.first-row-content__mobile {
width: ${(props) => (props.displayType === "aside" ? "147px" : "234px")};
height: ${(props) => (props.displayType === "aside" ? "16px" : "10px")};
}
@media ${desktop} {
.second-row-content__mobile {
max-width: 185px;
height: 8px;
display: block;
}
.row-content {
grid-template-rows: 10px;
grid-row-gap: 6px;
margin-top: -3px;
}
}
.second-row-content__mobile {
width: 229px;
}
}
.loader-wrapper_margin {
margin-left: ${(props) =>
props.displayType === "aside" ? "16px" : "12px"};
}
.select-file-dialog_modal-loader {
height: 290px;
padding-top: 16px;
box-sizing: border-box;
}
.panel-loader {
display: inline;
margin-right: 10px;
}
.modal-dialog_tree-body {
grid-area: tree;
}
.modal-dialog_files-body {
grid-area: files-list;
}
.modal-dialog_body {
display: grid;
grid-template-columns: 228px 477px;
height: 295px;
grid-template-areas: "tree files-list";
.modal-dialog_tree-body {
padding-top: 0;
border-right: ${(props) =>
props.theme.filesPanels.selectFile.borderRight};
span.rc-tree-title {
max-width: ${(props) =>
props.displayType === "aside" ? "243px" : "181px"};
}
}
}
.select-file-dialog-aside_buttons {
position: fixed;
bottom: 0;
padding-top: 8px;
background-color: ${(props) =>
props.theme.filesPanels.selectFile.buttonsBackground};
height: 40px;
width: 100%;
}
.select-file-dialog-buttons-save {
margin-right: 8px;
}
.select-file-modal-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-modal_buttons {
margin-top: 8px;
}
`;
StyledSelectFilePanel.defaultProps = { theme: Base };
const StyledFilesList = styled.div`
.select-file-dialog_icon {
margin-right: 8px;
}
.radio-button_text{
${(props) => props.displayType === "aside" && "margin: 0 !important;"};
}
.entry-title {
font-weight: 600;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.files-list_file-owner {
max-width: ${(props) =>
props.displayType === "aside" ? "213px" : "406px"};
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: ${(props) => props.theme.filesPanels.filesList.color};
font-weight: 600;
${(props) => props.displayType === "modal" && ` font-size: 11px;`}
height: ${(props) => (props.displayType === "aside" ? "16px" : "12px")};
padding-bottom: ${(props) =>
props.displayType === "aside" ? "10px" : "11px"};
}
.file-exst {
color: ${(props) => props.theme.filesPanels.filesList.color};
font-weight: 600;
}
.modal-dialog_file-name:hover {
background-color: ${(props) =>
props.theme.filesPanels.filesList.backgroundColor};
}
.files-list_full-name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: ${(props) => props.displayType === "aside" && "213px"};
grid-area: full-name;
display: flex;
padding-top: 10px;
}
.select-file-dialog_icon {
grid-area: icon-name;
padding-top: 12px;
}
.select-file-dialog_checked {
grid-area: checked-button;
}
.files-list_file-children_wrapper {
grid-area: owner-name;
margin-top: ${(props) => props.displayType === "modal" && "-8px"};
}
.modal-dialog_file-name {
border-radius: 3px;
padding-right: 12px;
${(props) =>
props.isChecked &&
`background: ${props.theme.filesPanels.filesList.backgroundColor};`}
cursor: pointer;
border-bottom: ${(props) => props.theme.filesPanels.filesList.borderBottom};
display: grid;
${(props) =>
props.displayType === "aside"
? css`
height: 56px;
grid-template-areas: "checked-button icon-name full-name" "checked-button icon-name owner-name";
`
: css`
height: 49px;
grid-template-areas: "checked-button icon-name full-name" "checked-button icon-name owner-name";
`}
grid-template-columns: 22px 33px 1fr;
${(props) => props.displayType === "modal" && ` grid-row-gap: 4px;`}
padding-left: ${(props) =>
props.displayType === "aside" ? "16px" : "12px"};
box-sizing: border-box;
}
`;
StyledFilesList.defaultProps = { theme: Base };
const StyledModalRowContainer = styled.div`
display: flex;
flex-direction: column;
@ -1132,8 +788,5 @@ export {
StyledSharingBody,
StyledFooter,
StyledLinkRow,
StyledSelectFolderPanel,
StyledSelectFilePanel,
StyledFilesList,
StyledModalRowContainer,
};

View File

@ -1,6 +1,6 @@
import { makeObservable, action, observable } from "mobx";
class SelectedFilesStore {
class SelectFileDialogStore {
folderId = null;
fileInfo = null;
@ -22,4 +22,4 @@ class SelectedFilesStore {
};
}
export default new SelectedFilesStore();
export default new SelectFileDialogStore();

View File

@ -0,0 +1,36 @@
import { makeObservable, action, observable } from "mobx";
class SelectFolderDialogStore {
folderId = null;
fileInfo = null;
folderTitle = "";
providerKey = null;
baseFolderPath = "";
constructor() {
makeObservable(this, {
fileInfo: observable,
folderId: observable,
folderTitle: observable,
providerKey: observable,
setFolderId: action,
setProviderKey: action,
setFolderTitle: action,
});
}
setFolderId = (id) => {
this.folderId = id;
};
setFolderTitle = (title) => {
this.folderTitle = title;
};
setProviderKey = (providerKey) => {
this.providerKey = providerKey;
};
}
export default new SelectFolderDialogStore();

View File

@ -12,11 +12,12 @@ import PrimaryProgressDataStore from "./PrimaryProgressDataStore";
import VersionHistoryStore from "./VersionHistoryStore";
import DialogsStore from "./DialogsStore";
import selectedFilesStore from "./SelectedFilesStore";
import selectFolderDialogStore from "./SelectFolderDialogStore";
import ContextOptionsStore from "./ContextOptionsStore";
import HotkeyStore from "./HotkeyStore";
import store from "studio/store";
import InfoPanelStore from "./InfoPanelStore";
import selectFileDialogStore from "./SelectFileDialogStore";
const selectedFolderStore = new SelectedFolderStore(store.auth.settingsStore);
@ -30,7 +31,8 @@ const filesStore = new FilesStore(
selectedFolderStore,
treeFoldersStore,
settingsStore,
selectedFilesStore
selectFolderDialogStore,
selectFileDialogStore
);
const mediaViewerDataStore = new MediaViewerDataStore(
filesStore,
@ -91,7 +93,6 @@ const hotkeyStore = new HotkeyStore(
uploadDataStore
);
//const selectedFilesStore = new SelectedFilesStore(selectedFilesStore);
const stores = {
filesStore,
settingsStore,
@ -102,10 +103,11 @@ const stores = {
treeFoldersStore,
selectedFolderStore,
filesActionsStore,
selectedFilesStore,
selectFolderDialogStore,
contextOptionsStore,
hotkeyStore,
infoPanelStore,
selectFileDialogStore,
};
export default stores;

View File

@ -169,10 +169,14 @@ var config = {
"./app": "./src/Files.jsx",
"./SharingDialog": "./src/components/panels/SharingDialog",
"./utils": "./src/helpers/utils.js",
"./SelectFileDialog": "./src/components/panels/SelectFileDialog",
"./SelectFolderDialog": "./src/components/panels/SelectFolderDialog",
"./SelectFolderInput": "./src/components/panels/SelectFolderInput",
"./SelectFileInput": "./src/components/panels/SelectFileInput",
"./SelectFileDialog":
"./src/components/panels/SelectFileDialog/SelectFileDialogWrapper",
"./SelectFileInput":
"./src/components/panels/SelectFileInput/SelectFileInputWrapper",
"./SelectFolderDialog":
"./src/components/panels/SelectFolderDialog/SelectFolderDialogWrapper",
"./SelectFolderInput":
"./src/components/panels/SelectFolderInput/SelectFolderInputWrapper",
},
shared: {
...deps,

View File

@ -1401,11 +1401,11 @@ namespace ASC.Files.Core.Data
{
if (withSubfolders)
{
result.In(a => a.Folders.Select(r => r.ParentId), new[] { parentId });
result.In(a => a.Folders.Select(r => r.FolderId), new[] { parentId });
}
else
{
result.InAll(a => a.Folders.Select(r => r.ParentId), new[] { parentId });
result.InAll(a => a.Folders.Select(r => r.FolderId), new[] { parentId });
}
}

View File

@ -777,7 +777,7 @@ ctx.Tag
}
monitorFolderIds = monitorFolderIds.Concat(tempTags.Where(x => x.EntryType == FileEntryType.Folder).Select(x => x.EntryId));
result.Concat(tempTags);
result = result.Concat(tempTags);
}
var monitorFolderIdsInt = await monitorFolderIds.OfType<int>().ToListAsync();
@ -787,13 +787,13 @@ ctx.Tag
var monitorFolderIdsStrings = await monitorFolderIds.Select(r => r.ToString()).ToListAsync();
result.Concat(FromQueryAsync(newTagsForFoldersQuery(FilesDbContext, tenantId, subject, monitorFolderIdsStrings)));
result = result.Concat(FromQueryAsync(newTagsForFoldersQuery(FilesDbContext, tenantId, subject, monitorFolderIdsStrings)));
var where = (deepSearch ? await monitorFolderIds.ToArrayAsync().ConfigureAwait(false) : new object[] { parentFolder.ID })
.Select(r => r.ToString())
.ToList();
result.Concat(FromQueryAsync(newTagsForFilesQuery(FilesDbContext, tenantId, subject, where)));
result = result.Concat(FromQueryAsync(newTagsForFilesQuery(FilesDbContext, tenantId, subject, where)));
if (parentFolder.FolderType == FolderType.USER || parentFolder.FolderType == FolderType.COMMON)
{
@ -809,7 +809,7 @@ ctx.Tag
if (thirdpartyFolderIds.Count > 0)
{
result.Concat(FromQueryAsync(newTagsForSBoxQuery(FilesDbContext, tenantId, subject, thirdpartyFolderIds)));
result = result.Concat(FromQueryAsync(newTagsForSBoxQuery(FilesDbContext, tenantId, subject, thirdpartyFolderIds)));
}
}

View File

@ -1601,7 +1601,7 @@ namespace ASC.Web.Files.Services.WCFService
{
if (sync)
{
results.Append(await FileConverter.ExecSynchronouslyAsync(file, fileInfo.Password));
results = results.Append(await FileConverter.ExecSynchronouslyAsync(file, fileInfo.Password));
}
else
{
@ -1999,8 +1999,8 @@ namespace ASC.Web.Files.Services.WCFService
var fileDao = GetFileDao();
var folderDao = GetFolderDao();
entries.Concat(filesId.ToAsyncEnumerable().SelectAwait(async fileId => await fileDao.GetFileAsync(fileId)));
entries.Concat(foldersId.ToAsyncEnumerable().SelectAwait(async e => await folderDao.GetFolderAsync(e)));
entries = entries.Concat(filesId.ToAsyncEnumerable().SelectAwait(async fileId => await fileDao.GetFileAsync(fileId)));
entries = entries.Concat(foldersId.ToAsyncEnumerable().SelectAwait(async e => await folderDao.GetFolderAsync(e)));
return FileSharingAceHelper.RemoveAceAsync(entries);
@ -2234,16 +2234,16 @@ namespace ASC.Web.Files.Services.WCFService
//return new List<string>(accounts);
}
public async IAsyncEnumerable<FileEntry> ChangeOwnerAsync(IEnumerable<T> foldersId, IEnumerable<T> filesId, Guid userId)
public async Task<IEnumerable<FileEntry>> ChangeOwnerAsync(IEnumerable<T> foldersId, IEnumerable<T> filesId, Guid userId)
{
var userInfo = UserManager.GetUsers(userId);
ErrorIf(Equals(userInfo, Constants.LostUser) || userInfo.IsVisitor(UserManager), FilesCommonResource.ErrorMassage_ChangeOwner);
var entries = AsyncEnumerable.Empty<FileEntry>();
var entries = new List<FileEntry>();
var folderDao = GetFolderDao();
var folders = folderDao.GetFoldersAsync(foldersId);
await foreach (var folder in folders)
var folders = await folderDao.GetFoldersAsync(foldersId).ToListAsync();
foreach (var folder in folders)
{
ErrorIf(!await FileSecurity.CanEditAsync(folder), FilesCommonResource.ErrorMassage_SecurityException);
ErrorIf(folder.RootFolderType != FolderType.COMMON, FilesCommonResource.ErrorMassage_SecurityException);
@ -2262,13 +2262,13 @@ namespace ASC.Web.Files.Services.WCFService
FilesMessageService.Send(newFolder, GetHttpHeaders(), MessageAction.FileChangeOwner, new[] { newFolder.Title, userInfo.DisplayUserName(false, DisplayUserSettingsHelper) });
}
entries.Append(newFolder);
entries.Add(newFolder);
}
var fileDao = GetFileDao();
var files = fileDao.GetFilesAsync(filesId);
var files = await fileDao.GetFilesAsync(filesId).ToListAsync();
await foreach (var file in files)
foreach (var file in files)
{
ErrorIf(!await FileSecurity.CanEditAsync(file), FilesCommonResource.ErrorMassage_SecurityException);
ErrorIf(await EntryManager.FileLockedForMeAsync(file.ID), FilesCommonResource.ErrorMassage_LockedFile);
@ -2313,13 +2313,10 @@ namespace ASC.Web.Files.Services.WCFService
FilesMessageService.Send(newFile, GetHttpHeaders(), MessageAction.FileChangeOwner, new[] { newFile.Title, userInfo.DisplayUserName(false, DisplayUserSettingsHelper) });
}
entries.Append(newFile);
entries.Add(newFile);
}
await foreach (var entrie in entries)
{
yield return entrie;
}
return entries;
}
public bool StoreOriginal(bool set)

View File

@ -124,7 +124,7 @@ namespace ASC.Files.Thirdparty.ProviderDao
var selectorLocal = selector;
var matchedIds = folderIds.Where(selectorLocal.IsMatch).ToList();
if (matchedIds.Count > 0) continue;
if (matchedIds.Count == 0) continue;
result = result.Concat(matchedIds.GroupBy(selectorLocal.GetIdCode)
.ToAsyncEnumerable()

View File

@ -75,17 +75,13 @@ namespace ASC.Web.Files
{
public class FileHandler
{
private IServiceProvider ServiceProvider { get; }
public FileHandler(RequestDelegate next, IServiceProvider serviceProvider)
public FileHandler(RequestDelegate next)
{
ServiceProvider = serviceProvider;
}
public async Task Invoke(HttpContext context)
public async Task Invoke(HttpContext context, FileHandlerService fileHandlerService)
{
using var scope = ServiceProvider.CreateScope();
var fileHandlerService = scope.ServiceProvider.GetService<FileHandlerService>();
await fileHandlerService.Invoke(context).ConfigureAwait(false);
}
}

View File

@ -663,7 +663,7 @@ namespace ASC.Web.Files.Utils
public Task<Stream> ExecAsync<T>(File<T> file)
{
return ExecAsync(file, FileUtility.GetInternalExtension(file.Title));
}
}
public async Task<Stream> ExecAsync<T>(File<T> file, string toExtension, string password = null)
{
@ -687,8 +687,8 @@ namespace ASC.Web.Files.Utils
var request = new HttpRequestMessage();
request.RequestUri = new Uri(convertUri);
using var httpClient = ClientFactory.CreateClient();
using var response = await httpClient.SendAsync(request);
var httpClient = ClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
return new ResponseStream(response);
}

View File

@ -151,29 +151,29 @@ namespace ASC.Web.Files.Utils
if (obj.FileEntry.RootFolderType == FolderType.BUNCH)
{
if (userIDs.Count == 0) return;
var projectsFolder = await GlobalFolder.GetFolderProjectsAsync<T>(DaoFactory);
parentFolders.Add(await folderDao.GetFolderAsync(projectsFolder));
var entries = new List<FileEntry> { obj.FileEntry };
entries = entries.Concat(parentFolders).ToList();
userIDs.ForEach(userID =>
userIDs.ForEach(userID =>
{
if (userEntriesData.TryGetValue(userID, out var value))
value.AddRange(entries);
else
userEntriesData.Add(userID, entries);
RemoveFromCahce(projectsFolder, userID);
userEntriesData.Add(userID, entries);
RemoveFromCahce(projectsFolder, userID);
});
}
else
{
var filesSecurity = FileSecurity;
if (userIDs.Count == 0)
{
{
var guids = await filesSecurity.WhoCanReadAsync(obj.FileEntry);
userIDs = guids.Where(x => x != obj.CurrentAccountId).ToList();
}
@ -183,7 +183,7 @@ namespace ASC.Web.Files.Utils
}
foreach (var parentFolder in parentFolders)
{
{
var whoCanRead = await filesSecurity.WhoCanReadAsync(parentFolder);
var ids = whoCanRead
.Where(userID => userIDs.Contains(userID) && userID != obj.CurrentAccountId);
@ -199,7 +199,7 @@ namespace ASC.Web.Files.Utils
if (obj.FileEntry.RootFolderType == FolderType.USER)
{
var folderDaoInt = DaoFactory.GetFolderDao<int>();
var folderDaoInt = DaoFactory.GetFolderDao<int>();
var folderShare = await folderDaoInt.GetFolderAsync(await GlobalFolder.GetFolderShareAsync(DaoFactory));
foreach (var userID in userIDs)
@ -232,46 +232,46 @@ namespace ASC.Web.Files.Utils
RemoveFromCahce(rootFolder.ID, userID);
}
}
}
else if (obj.FileEntry.RootFolderType == FolderType.COMMON)
{
{
var commonFolderId = await GlobalFolder.GetFolderCommonAsync(this, DaoFactory);
userIDs.ForEach(userID => RemoveFromCahce(commonFolderId, userID));
if (obj.FileEntry.ProviderEntry)
{
var commonFolder = await folderDao.GetFolderAsync(await GlobalFolder.GetFolderCommonAsync<T>(this, DaoFactory));
userIDs.ForEach(userID =>
userIDs.ForEach(userID =>
{
if (userEntriesData.TryGetValue(userID, out var value))
value.Add(commonFolder);
else
userEntriesData.Add(userID, new List<FileEntry> { commonFolder });
RemoveFromCahce(commonFolderId, userID);
userEntriesData.Add(userID, new List<FileEntry> { commonFolder });
RemoveFromCahce(commonFolderId, userID);
});
}
}
else if (obj.FileEntry.RootFolderType == FolderType.Privacy)
{
foreach (var userID in userIDs)
{
var privacyFolderId = await folderDao.GetFolderIDPrivacyAsync(false, userID);
if (Equals(privacyFolderId, 0)) continue;
var rootFolder = await folderDao.GetFolderAsync(privacyFolderId);
if (rootFolder == null) continue;
if (userEntriesData.TryGetValue(userID, out var value))
value.Add(rootFolder);
else
userEntriesData.Add(userID, new List<FileEntry> { rootFolder });
RemoveFromCahce(rootFolder.ID, userID);
}
}
else if (obj.FileEntry.RootFolderType == FolderType.Privacy)
{
foreach (var userID in userIDs)
{
var privacyFolderId = await folderDao.GetFolderIDPrivacyAsync(false, userID);
if (Equals(privacyFolderId, 0)) continue;
var rootFolder = await folderDao.GetFolderAsync(privacyFolderId);
if (rootFolder == null) continue;
if (userEntriesData.TryGetValue(userID, out var value))
value.Add(rootFolder);
else
userEntriesData.Add(userID, new List<FileEntry> { rootFolder });
RemoveFromCahce(rootFolder.ID, userID);
}
}
userIDs.ForEach(userID =>
userIDs.ForEach(userID =>
{
if (userEntriesData.TryGetValue(userID, out var value))
value.Add(obj.FileEntry);
@ -291,49 +291,49 @@ namespace ASC.Web.Files.Utils
var entries = userEntriesData[userID].Distinct().ToList();
await GetNewTagsAsync(userID, entries.OfType<FileEntry<int>>().ToList());
await GetNewTagsAsync(userID, entries.OfType<FileEntry<int>>().ToList());
await GetNewTagsAsync(userID, entries.OfType<FileEntry<string>>().ToList());
}
if (updateTags.Count > 0)
{
tagDao.UpdateNewTags(updateTags);
}
if (updateTags.Count > 0)
{
tagDao.UpdateNewTags(updateTags);
}
if (newTags.Count > 0)
{
tagDao.SaveTags(newTags);
}
async Task GetNewTagsAsync<T1>(Guid userID, List<FileEntry<T1>> entries)
{
var tagDao1 = DaoFactory.GetTagDao<T1>();
if (newTags.Count > 0)
{
tagDao.SaveTags(newTags);
}
async Task GetNewTagsAsync<T1>(Guid userID, List<FileEntry<T1>> entries)
{
var tagDao1 = DaoFactory.GetTagDao<T1>();
var exist = await tagDao1.GetNewTagsAsync(userID, entries).ToListAsync();
var update = exist.Where(t => t.EntryType == FileEntryType.Folder).ToList();
update.ForEach(t => t.Count++);
updateTags.AddRange(update);
entries.ForEach(entry =>
{
if (entry != null && exist.All(tag => tag != null && !tag.EntryId.Equals(entry.ID)))
{
newTags.Add(Tag.New(userID, entry));
}
entries.ForEach(entry =>
{
if (entry != null && exist.All(tag => tag != null && !tag.EntryId.Equals(entry.ID)))
{
newTags.Add(Tag.New(userID, entry));
}
});
}
}
}
public Task MarkAsNewAsync<T>(FileEntry<T> fileEntry, List<Guid> userIDs = null)
{
if (CoreBaseSettings.Personal) return Task.CompletedTask;
if (fileEntry == null) return Task.CompletedTask;
if (fileEntry == null) return Task.CompletedTask;
return InternalMarkAsNewAsync(fileEntry, userIDs);
}
private async Task InternalMarkAsNewAsync<T>(FileEntry<T> fileEntry, List<Guid> userIDs = null)
{
}
private async Task InternalMarkAsNewAsync<T>(FileEntry<T> fileEntry, List<Guid> userIDs = null)
{
userIDs ??= new List<Guid>();
var taskData = ServiceProvider.GetService<AsyncTaskData<T>>();
@ -347,7 +347,7 @@ namespace ASC.Web.Files.Utils
var projectID = path.Split('/').Last();
if (string.IsNullOrEmpty(projectID)) return;
var whoCanRead = await FileSecurity.WhoCanReadAsync(fileEntry);
var projectTeam = whoCanRead.Where(x => x != AuthContext.CurrentAccount.ID).ToList();
@ -356,33 +356,32 @@ namespace ASC.Web.Files.Utils
taskData.UserIDs = projectTeam;
}
ServiceProvider.GetService<FileMarkerHelper<T>>().Add(taskData);
}
ServiceProvider.GetService<FileMarkerHelper<T>>().Add(taskData);
}
public Task RemoveMarkAsNewAsync<T>(FileEntry<T> fileEntry, Guid userID = default)
{
if (CoreBaseSettings.Personal) return Task.CompletedTask;
if (fileEntry == null) return Task.CompletedTask;
if (fileEntry == null) return Task.CompletedTask;
return InternalRemoveMarkAsNewAsync(fileEntry, userID);
}
public async Task InternalRemoveMarkAsNewAsync<T>(FileEntry<T> fileEntry, Guid userID = default)
{
userID = userID.Equals(default) ? AuthContext.CurrentAccount.ID : userID;
}
public async Task InternalRemoveMarkAsNewAsync<T>(FileEntry<T> fileEntry, Guid userID = default)
{
userID = userID.Equals(default) ? AuthContext.CurrentAccount.ID : userID;
var tagDao = DaoFactory.GetTagDao<T>();
var internalFolderDao = DaoFactory.GetFolderDao<int>();
var folderDao = DaoFactory.GetFolderDao<T>();
var internalFolderDao = DaoFactory.GetFolderDao<int>();
var folderDao = DaoFactory.GetFolderDao<T>();
if (!await tagDao.GetNewTagsAsync(userID, fileEntry).AnyAsync()) return;
T folderID;
int valueNew;
var userFolderIdTask = internalFolderDao.GetFolderIDUserAsync(false, userID);
var privacyFolderIdTask = internalFolderDao.GetFolderIDPrivacyAsync(false, userID);
var userFolderId = await userFolderIdTask;
var userFolderId = await internalFolderDao.GetFolderIDUserAsync(false, userID);
var privacyFolderId = await internalFolderDao.GetFolderIDPrivacyAsync(false, userID);
var removeTags = new List<Tag>();
@ -417,8 +416,6 @@ namespace ASC.Web.Files.Utils
removeTags.AddRange(listTags);
}
var privacyFolderId = await privacyFolderIdTask;
var parentFolders = await folderDao.GetParentFoldersAsync(folderID);
parentFolders.Reverse();
@ -449,20 +446,20 @@ namespace ASC.Web.Files.Utils
cacheFolderId = rootFolderId = await GlobalFolder.GetFolderShareAsync(DaoFactory);
else
cacheFolderId = userFolderId;
}
else if (rootFolder.RootFolderType == FolderType.Privacy)
{
if (!Equals(privacyFolderId, 0))
{
cacheFolderId = rootFolderId = privacyFolderId;
}
}
else if (rootFolder.RootFolderType == FolderType.Privacy)
{
if (!Equals(privacyFolderId, 0))
{
cacheFolderId = rootFolderId = privacyFolderId;
}
}
else if (rootFolder.RootFolderType == FolderType.SHARE)
{
cacheFolderId = await GlobalFolder.GetFolderShareAsync(DaoFactory);
}
var updateTags = new List<Tag>();
var updateTags = new List<Tag>();
if (!rootFolderId.Equals(default))
{
@ -482,12 +479,12 @@ namespace ASC.Web.Files.Utils
if (updateTags.Count > 0)
tagDao.UpdateNewTags(updateTags);
if (removeTags.Count > 0)
tagDao.RemoveTags(removeTags);
async Task UpdateRemoveTags<TFolder>(Folder<TFolder> folder)
{
var tagDao = DaoFactory.GetTagDao<TFolder>();
var newTags = tagDao.GetNewTagsAsync(userID, folder);
tagDao.RemoveTags(removeTags);
async Task UpdateRemoveTags<TFolder>(Folder<TFolder> folder)
{
var tagDao = DaoFactory.GetTagDao<TFolder>();
var newTags = tagDao.GetNewTagsAsync(userID, folder);
var parentTag = await newTags.FirstOrDefaultAsync();
if (parentTag != null)
@ -503,9 +500,9 @@ namespace ASC.Web.Files.Utils
removeTags.Add(parentTag);
}
}
}
}
}
}
public async Task RemoveMarkAsNewForAllAsync<T>(FileEntry<T> fileEntry)
{
IAsyncEnumerable<Guid> userIDs;
@ -518,9 +515,9 @@ namespace ASC.Web.Files.Utils
{
await RemoveMarkAsNewAsync(fileEntry, userID);
}
}
}
public async Task<int> GetRootFoldersIdMarkedAsNewAsync<T>(T rootId)
{
var fromCache = GetCountFromCahce(rootId);
@ -541,118 +538,126 @@ namespace ASC.Web.Files.Utils
}
return 0;
}
}
public Task<List<FileEntry>> MarkedItemsAsync<T>(Folder<T> folder)
{
if (folder == null) throw new ArgumentNullException(nameof(folder), FilesCommonResource.ErrorMassage_FolderNotFound);
return InternalMarkedItemsAsync(folder);
}
private async Task<List<FileEntry>> InternalMarkedItemsAsync<T>(Folder<T> folder)
{
}
private async Task<List<FileEntry>> InternalMarkedItemsAsync<T>(Folder<T> folder)
{
if (!await FileSecurity.CanReadAsync(folder)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ViewFolder);
if (folder.RootFolderType == FolderType.TRASH && !Equals(folder.ID, await GlobalFolder.GetFolderTrashAsync<T>(DaoFactory))) throw new SecurityException(FilesCommonResource.ErrorMassage_ViewTrashItem);
if (folder.RootFolderType == FolderType.TRASH && !Equals(folder.ID, await GlobalFolder.GetFolderTrashAsync<T>(DaoFactory))) throw new SecurityException(FilesCommonResource.ErrorMassage_ViewTrashItem);
var tagDao = DaoFactory.GetTagDao<T>();
var providerFolderDao = DaoFactory.GetFolderDao<string>();
var providerFolderDao = DaoFactory.GetFolderDao<string>();
var providerTagDao = DaoFactory.GetTagDao<string>();
var tags = (tagDao.GetNewTagsAsync(AuthContext.CurrentAccount.ID, folder, true) ?? AsyncEnumerable.Empty<Tag>());
var tags = await (tagDao.GetNewTagsAsync(AuthContext.CurrentAccount.ID, folder, true) ?? AsyncEnumerable.Empty<Tag>()).ToListAsync();
if (!(await tags.CountAsync() == 0)) return new List<FileEntry>();
if (!tags.Any()) return new List<FileEntry>();
if (Equals(folder.ID, GlobalFolder.GetFolderMy(this, DaoFactory)) ||
Equals(folder.ID, await GlobalFolder.GetFolderCommonAsync(this, DaoFactory)) ||
if (Equals(folder.ID, GlobalFolder.GetFolderMy(this, DaoFactory)) ||
Equals(folder.ID, await GlobalFolder.GetFolderCommonAsync(this, DaoFactory)) ||
Equals(folder.ID, await GlobalFolder.GetFolderShareAsync(DaoFactory)))
{
var folderTags = tags.Where(tag => tag.EntryType == FileEntryType.Folder && (tag.EntryId is string));
var providerFolderTags = folderTags
.SelectAwait(async tag => new KeyValuePair<Tag, Folder<string>>(tag, await providerFolderDao.GetFolderAsync(tag.EntryId.ToString())))
.Where(pair => pair.Value != null && pair.Value.ProviderEntry);
var providerFolderTags = new List<KeyValuePair<Tag, Folder<string>>>();
providerFolderTags = providerFolderTags.Reverse();
await foreach (var providerFolderTag in providerFolderTags)
foreach (var tag in folderTags)
{
tags.Concat(providerTagDao.GetNewTagsAsync(AuthContext.CurrentAccount.ID, providerFolderTag.Value, true));
var pair = new KeyValuePair<Tag, Folder<string>>(tag, await providerFolderDao.GetFolderAsync(tag.EntryId.ToString()));
if (pair.Value != null && pair.Value.ProviderEntry)
{
providerFolderTags.Add(pair);
}
}
providerFolderTags.Reverse();
foreach (var providerFolderTag in providerFolderTags)
{
tags.AddRange(await providerTagDao.GetNewTagsAsync(AuthContext.CurrentAccount.ID, providerFolderTag.Value, true).ToListAsync());
}
}
tags = tags
.Where(r => !Equals(r.EntryId, folder.ID))
.Distinct();
tags = tags
.Where(r => !Equals(r.EntryId, folder.ID))
.Distinct()
.ToList();
//TODO: refactoring
var entryTagsProvider = await GetEntryTagsAsync<string>(tags.Where(r => r.EntryId is string));
var entryTagsInternal = await GetEntryTagsAsync<int>(tags.Where(r => r.EntryId is int));
//TODO: refactoring
var entryTagsProvider = await GetEntryTagsAsync<string>(tags.Where(r => r.EntryId is string).ToAsyncEnumerable());
var entryTagsInternal = await GetEntryTagsAsync<int>(tags.Where(r => r.EntryId is int).ToAsyncEnumerable());
foreach (var entryTag in entryTagsInternal)
{
var parentEntry = entryTagsInternal.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, entryTag.Key.FolderID));
if (parentEntry != null)
{
entryTagsInternal[parentEntry].Count -= entryTag.Value.Count;
}
}
foreach (var entryTag in entryTagsProvider)
{
if (int.TryParse(entryTag.Key.FolderID, out var fId))
{
var parentEntryInt = entryTagsInternal.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, fId));
if (parentEntryInt != null)
{
entryTagsInternal[parentEntryInt].Count -= entryTag.Value.Count;
}
continue;
}
var parentEntry = entryTagsProvider.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, entryTag.Key.FolderID));
var parentEntry = entryTagsInternal.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, entryTag.Key.FolderID));
if (parentEntry != null)
{
entryTagsProvider[parentEntry].Count -= entryTag.Value.Count;
{
entryTagsInternal[parentEntry].Count -= entryTag.Value.Count;
}
}
foreach (var entryTag in entryTagsProvider)
{
if (int.TryParse(entryTag.Key.FolderID, out var fId))
{
var parentEntryInt = entryTagsInternal.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, fId));
if (parentEntryInt != null)
{
entryTagsInternal[parentEntryInt].Count -= entryTag.Value.Count;
}
continue;
}
var parentEntry = entryTagsProvider.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, entryTag.Key.FolderID));
if (parentEntry != null)
{
entryTagsProvider[parentEntry].Count -= entryTag.Value.Count;
}
}
var result = new List<FileEntry>();
await GetResultAsync(entryTagsInternal);
await GetResultAsync(entryTagsInternal);
await GetResultAsync(entryTagsProvider);
return result;
async Task GetResultAsync<TEntry>(Dictionary<FileEntry<TEntry>, Tag> entryTags)
{
foreach (var entryTag in entryTags)
{
if (!string.IsNullOrEmpty(entryTag.Key.Error))
{
await RemoveMarkAsNewAsync(entryTag.Key);
continue;
}
if (entryTag.Value.Count > 0)
{
result.Add(entryTag.Key);
}
return result;
async Task GetResultAsync<TEntry>(Dictionary<FileEntry<TEntry>, Tag> entryTags)
{
foreach (var entryTag in entryTags)
{
if (!string.IsNullOrEmpty(entryTag.Key.Error))
{
await RemoveMarkAsNewAsync(entryTag.Key);
continue;
}
if (entryTag.Value.Count > 0)
{
result.Add(entryTag.Key);
}
}
}
}
private async Task<Dictionary<FileEntry<T>, Tag>> GetEntryTagsAsync<T>(IAsyncEnumerable<Tag> tags)
}
}
private async Task<Dictionary<FileEntry<T>, Tag>> GetEntryTagsAsync<T>(IAsyncEnumerable<Tag> tags)
{
var fileDao = DaoFactory.GetFileDao<T>();
var folderDao = DaoFactory.GetFolderDao<T>();
var entryTags = new Dictionary<FileEntry<T>, Tag>();
var folderDao = DaoFactory.GetFolderDao<T>();
var entryTags = new Dictionary<FileEntry<T>, Tag>();
await foreach (var tag in tags)
{
@ -667,11 +672,11 @@ namespace ASC.Web.Files.Utils
{
//todo: RemoveMarkAsNew(tag);
}
}
return entryTags;
}
}
return entryTags;
}
public async Task<IEnumerable<FileEntry>> SetTagsNewAsync<T>(Folder<T> parent, IEnumerable<FileEntry> entries)
{
var tagDao = DaoFactory.GetTagDao<T>();

View File

@ -184,8 +184,11 @@ namespace ASC.Web.Files.Utils
await NotifyClient.SendShareNoticeAsync(entry, recipients, message);
}
}
await usersWithoutRight.ToAsyncEnumerable().ForEachAwaitAsync(async userId => await FileMarker.RemoveMarkAsNewAsync(entry, userId));
foreach (var userId in usersWithoutRight)
{
await FileMarker.RemoveMarkAsNewAsync(entry, userId);
}
return changed;
}
@ -199,9 +202,9 @@ namespace ASC.Web.Files.Utils
{
if (entry.RootFolderType != FolderType.USER && entry.RootFolderType != FolderType.Privacy
|| Equals(entry.RootFolderId, GlobalFolderHelper.FolderMy)
|| Equals(entry.RootFolderId, await GlobalFolderHelper.FolderPrivacyAsync))
|| Equals(entry.RootFolderId, await GlobalFolderHelper.FolderPrivacyAsync))
return;
var entryType = entry.FileEntryType;
await fileSecurity.ShareAsync(entry.ID, entryType, AuthContext.CurrentAccount.ID,
entry.RootFolderType == FolderType.USER
@ -335,11 +338,11 @@ namespace ASC.Web.Files.Utils
isgroup = true;
title = g.Name;
if (g.ID == Constants.GroupAdmin.ID)
if (g.ID == Constants.GroupAdmin.ID)
title = FilesCommonResource.Admin;
if (g.ID == Constants.GroupEveryone.ID)
title = FilesCommonResource.Everyone;
if (g.ID == Constants.LostGroupInfo.ID)
{
await fileSecurity.RemoveSubjectAsync<T>(r.Subject);

View File

@ -168,7 +168,7 @@ namespace ASC.Api.Documents
}
[Read("@root")]
public async Task<IEnumerable<FolderContentWrapper<int>>> GetRootFoldersAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders, bool withoutTrash, bool withoutAdditionalFolder)
public async Task<IEnumerable<FolderContentWrapper<int>>> GetRootFoldersAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders, bool withoutTrash, bool withoutAdditionalFolder)
{
var IsVisitor = UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor(UserManager);
var IsOutsider = UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsOutsider(UserManager);
@ -229,22 +229,22 @@ namespace ASC.Api.Documents
var result = new List<FolderContentWrapper<int>>();
foreach (var folder in folders)
{
result.Add(await FilesControllerHelperInt.GetFolderAsync(folder, userIdOrGroupId, filterType, withsubfolders));
result.Add(await FilesControllerHelperInt.GetFolderAsync(folder, userIdOrGroupId, filterType, searchInContent, withsubfolders));
}
return result;
}
[Read("@privacy")]
public Task<FolderContentWrapper<int>> GetPrivacyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public Task<FolderContentWrapper<int>> GetPrivacyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
if (!IsAvailablePrivacyRoomSettings()) throw new System.Security.SecurityException();
return InternalGetPrivacyFolderAsync(userIdOrGroupId, filterType, withsubfolders);
return InternalGetPrivacyFolderAsync(userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
private async Task<FolderContentWrapper<int>> InternalGetPrivacyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
private async Task<FolderContentWrapper<int>> InternalGetPrivacyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderPrivacyAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderPrivacyAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
[Read("@privacy/available")]
@ -262,9 +262,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>My folder contents</returns>
[Read("@my")]
public Task<FolderContentWrapper<int>> GetMyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public Task<FolderContentWrapper<int>> GetMyFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return FilesControllerHelperInt.GetFolderAsync(GlobalFolderHelper.FolderMy, userIdOrGroupId, filterType, withsubfolders);
return FilesControllerHelperInt.GetFolderAsync(GlobalFolderHelper.FolderMy, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -276,9 +276,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Projects folder contents</returns>
[Read("@projects")]
public async Task<FolderContentWrapper<string>> GetProjectsFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<string>> GetProjectsFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperString.GetFolderAsync(await GlobalFolderHelper.GetFolderProjectsAsync<string>(), userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperString.GetFolderAsync(await GlobalFolderHelper.GetFolderProjectsAsync<string>(), userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
@ -291,9 +291,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Common folder contents</returns>
[Read("@common")]
public async Task<FolderContentWrapper<int>> GetCommonFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<int>> GetCommonFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderCommonAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderCommonAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -305,9 +305,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Shared folder contents</returns>
[Read("@share")]
public async Task<FolderContentWrapper<int>> GetShareFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<int>> GetShareFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderShareAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderShareAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -317,9 +317,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Recent contents</returns>
[Read("@recent")]
public async Task<FolderContentWrapper<int>> GetRecentFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<int>> GetRecentFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderRecentAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderRecentAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
[Create("file/{fileId}/recent", order: int.MaxValue)]
@ -341,9 +341,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Favorites contents</returns>
[Read("@favorites")]
public async Task<FolderContentWrapper<int>> GetFavoritesFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<int>> GetFavoritesFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderFavoritesAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderFavoritesAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -353,9 +353,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Templates contents</returns>
[Read("@templates")]
public async Task<FolderContentWrapper<int>> GetTemplatesFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<int>> GetTemplatesFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderTemplatesAsync, userIdOrGroupId, filterType, withsubfolders);
return await FilesControllerHelperInt.GetFolderAsync(await GlobalFolderHelper.FolderTemplatesAsync, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -367,9 +367,9 @@ namespace ASC.Api.Documents
/// <category>Folders</category>
/// <returns>Trash folder contents</returns>
[Read("@trash")]
public Task<FolderContentWrapper<int>> GetTrashFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public Task<FolderContentWrapper<int>> GetTrashFolderAsync(Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return FilesControllerHelperInt.GetFolderAsync(Convert.ToInt32(GlobalFolderHelper.FolderTrash), userIdOrGroupId, filterType, withsubfolders);
return FilesControllerHelperInt.GetFolderAsync(Convert.ToInt32(GlobalFolderHelper.FolderTrash), userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
/// <summary>
@ -384,16 +384,16 @@ namespace ASC.Api.Documents
/// <param name="filterType" optional="true" remark="Allowed values: None (0), FilesOnly (1), FoldersOnly (2), DocumentsOnly (3), PresentationsOnly (4), SpreadsheetsOnly (5) or ImagesOnly (7)">Filter type</param>
/// <returns>Folder contents</returns>
[Read("{folderId}", order: int.MaxValue, DisableFormat = true)]
public async Task<FolderContentWrapper<string>> GetFolderAsync(string folderId, Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public async Task<FolderContentWrapper<string>> GetFolderAsync(string folderId, Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
var folder = await FilesControllerHelperString.GetFolderAsync(folderId, userIdOrGroupId, filterType, withsubfolders);
var folder = await FilesControllerHelperString.GetFolderAsync(folderId, userIdOrGroupId, filterType, searchInContent, withsubfolders);
return folder.NotFoundIfNull();
}
[Read("{folderId:int}", order: int.MaxValue - 1, DisableFormat = true)]
public Task<FolderContentWrapper<int>> GetFolderAsync(int folderId, Guid userIdOrGroupId, FilterType filterType, bool withsubfolders)
public Task<FolderContentWrapper<int>> GetFolderAsync(int folderId, Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withsubfolders)
{
return FilesControllerHelperInt.GetFolderAsync(folderId, userIdOrGroupId, filterType, withsubfolders);
return FilesControllerHelperInt.GetFolderAsync(folderId, userIdOrGroupId, filterType, searchInContent, withsubfolders);
}
[Read("{folderId}/subfolders")]
@ -1058,31 +1058,35 @@ namespace ASC.Api.Documents
}
[Create("owner")]
public IAsyncEnumerable<FileEntryWrapper> ChangeOwnerFromBodyAsync([FromBody] ChangeOwnerModel model)
public async Task<IEnumerable<FileEntryWrapper>> ChangeOwnerFromBodyAsync([FromBody] ChangeOwnerModel model)
{
return ChangeOwnerAsync(model);
return await ChangeOwnerAsync(model);
}
[Create("owner")]
[Consumes("application/x-www-form-urlencoded")]
public IAsyncEnumerable<FileEntryWrapper> ChangeOwnerFromFormAsync([FromForm] ChangeOwnerModel model)
public async Task<IEnumerable<FileEntryWrapper>> ChangeOwnerFromFormAsync([FromForm] ChangeOwnerModel model)
{
return ChangeOwnerAsync(model);
return await ChangeOwnerAsync(model);
}
public async IAsyncEnumerable<FileEntryWrapper> ChangeOwnerAsync(ChangeOwnerModel model)
public async Task<IEnumerable<FileEntryWrapper>> ChangeOwnerAsync(ChangeOwnerModel model)
{
var (folderIntIds, folderStringIds) = FileOperationsManager.GetIds(model.FolderIds);
var (fileIntIds, fileStringIds) = FileOperationsManager.GetIds(model.FileIds);
var result = AsyncEnumerable.Empty<FileEntry>();
result.Concat(FileStorageServiceInt.ChangeOwnerAsync(folderIntIds, fileIntIds, model.UserId));
result.Concat(FileStorageService.ChangeOwnerAsync(folderStringIds, fileStringIds, model.UserId));
var data = Enumerable.Empty<FileEntry>();
data = data.Concat(await FileStorageServiceInt.ChangeOwnerAsync(folderIntIds, fileIntIds, model.UserId));
data = data.Concat(await FileStorageService.ChangeOwnerAsync(folderStringIds, fileStringIds, model.UserId));
await foreach (var e in result)
var result = new List<FileEntryWrapper>();
foreach (var e in data)
{
yield return await FilesControllerHelperInt.GetFileEntryWrapperAsync(e);
result.Add(await FilesControllerHelperInt.GetFileEntryWrapperAsync(e));
}
return result;
}
/// <summary>
@ -1144,41 +1148,41 @@ namespace ASC.Api.Documents
}
[Create("file/{fileId:int}/copyas", order: int.MaxValue - 1)]
public object CopyFileAsFromBody(int fileId, [FromBody] CopyAsModel<JsonElement> model)
public Task<FileEntryWrapper> CopyFileAsFromBody(int fileId, [FromBody] CopyAsModel<JsonElement> model)
{
return CopyFile(fileId, model);
}
[Create("file/{fileId:int}/copyas", order: int.MaxValue - 1)]
[Consumes("application/x-www-form-urlencoded")]
public object CopyFileAsFromForm(int fileId, [FromForm] CopyAsModel<JsonElement> model)
public Task<FileEntryWrapper> CopyFileAsFromForm(int fileId, [FromForm] CopyAsModel<JsonElement> model)
{
return CopyFile(fileId, model);
}
[Create("file/{fileId}/copyas", order: int.MaxValue)]
public object CopyFileAsFromBody(string fileId, [FromBody] CopyAsModel<JsonElement> model)
public Task<FileEntryWrapper> CopyFileAsFromBody(string fileId, [FromBody] CopyAsModel<JsonElement> model)
{
return CopyFile(fileId, model);
}
[Create("file/{fileId}/copyas", order: int.MaxValue)]
[Consumes("application/x-www-form-urlencoded")]
public object CopyFileAsFromForm(string fileId, [FromForm] CopyAsModel<JsonElement> model)
public Task<FileEntryWrapper> CopyFileAsFromForm(string fileId, [FromForm] CopyAsModel<JsonElement> model)
{
return CopyFile(fileId, model);
}
private object CopyFile<T>(T fileId, CopyAsModel<JsonElement> model)
private async Task<FileEntryWrapper> CopyFile<T>(T fileId, CopyAsModel<JsonElement> model)
{
var helper = ServiceProvider.GetService<FilesControllerHelper<T>>();
if (model.DestFolderId.ValueKind == JsonValueKind.Number)
{
return helper.CopyFileAsAsync(fileId, model.DestFolderId.GetInt32(), model.DestTitle, model.Password);
return await helper.CopyFileAsAsync(fileId, model.DestFolderId.GetInt32(), model.DestTitle, model.Password);
}
else if (model.DestFolderId.ValueKind == JsonValueKind.String)
{
return helper.CopyFileAsAsync(fileId, model.DestFolderId.GetString(), model.DestTitle, model.Password);
return await helper.CopyFileAsAsync(fileId, model.DestFolderId.GetString(), model.DestTitle, model.Password);
}
return null;

View File

@ -141,9 +141,9 @@ namespace ASC.Files.Helpers
ClientFactory = clientFactory;
}
public async Task<FolderContentWrapper<T>> GetFolderAsync(T folderId, Guid userIdOrGroupId, FilterType filterType, bool withSubFolders)
public async Task<FolderContentWrapper<T>> GetFolderAsync(T folderId, Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withSubFolders)
{
var folderContentWrapper = await ToFolderContentWrapperAsync(folderId, userIdOrGroupId, filterType, withSubFolders);
var folderContentWrapper = await ToFolderContentWrapperAsync(folderId, userIdOrGroupId, filterType, searchInContent, withSubFolders);
return folderContentWrapper.NotFoundIfNull();
}
@ -402,7 +402,6 @@ namespace ASC.Files.Helpers
public async Task<FileWrapper<TTemplate>> CopyFileAsAsync<TTemplate>(T fileId, TTemplate destFolderId, string destTitle, string password = null)
{
var service = ServiceProvider.GetService<FileStorageService<TTemplate>>();
var controller = ServiceProvider.GetService<FilesControllerHelper<TTemplate>>();
var file = await FileStorageService.GetFileAsync(fileId, -1);
var ext = FileUtility.GetFileExtension(file.Title);
var destExt = FileUtility.GetFileExtension(destTitle);
@ -415,6 +414,7 @@ namespace ASC.Files.Helpers
using (var fileStream = await FileConverter.ExecAsync(file, destExt, password))
{
var controller = ServiceProvider.GetService<FilesControllerHelper<TTemplate>>();
return await controller.InsertFileAsync(destFolderId, fileStream, destTitle, true);
}
}
@ -823,7 +823,7 @@ namespace ASC.Files.Helpers
// return files.Concat(folders);
//}
private async Task<FolderContentWrapper<T>> ToFolderContentWrapperAsync(T folderId, Guid userIdOrGroupId, FilterType filterType, bool withSubFolders)
private async Task<FolderContentWrapper<T>> ToFolderContentWrapperAsync(T folderId, Guid userIdOrGroupId, FilterType filterType, bool searchInContent, bool withSubFolders)
{
OrderBy orderBy = null;
if (Enum.TryParse(ApiContext.SortBy, true, out SortedByType sortBy))
@ -839,7 +839,7 @@ namespace ASC.Files.Helpers
filterType == FilterType.ByUser,
userIdOrGroupId.ToString(),
ApiContext.FilterValue,
false,
searchInContent,
withSubFolders,
orderBy);
return await FolderContentWrapperHelper.GetAsync(items, startIndex);

View File

@ -0,0 +1,32 @@
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_23674_260133)">
<path d="M11.9999 39.86H12.4999V39.36V22.08V21.58H11.9999H7.67986C6.22513 21.58 5.0186 22.2346 4.18295 23.1248C3.35539 24.0064 2.85986 25.1531 2.85986 26.1949V39.36V39.86H3.35986H11.9999Z" fill="#BDECFF" stroke="#333333"/>
<path d="M58.5596 21.58H58.0596V22.08V33.6V34.1H58.5596H67.1996H67.6996V33.6V25.8309C67.6996 23.1962 65.5055 21.58 63.3596 21.58H58.5596Z" fill="#BDECFF" stroke="#333333"/>
<path d="M53.7598 1.42H54.2598V1.92V40.32V40.82H53.7598H7.67976H7.17976V40.32V10.8808V10.6719L7.32834 10.5251L16.3976 1.56432L16.5437 1.42H16.749H53.7598Z" fill="white" stroke="#333333"/>
<path d="M15.9658 1.56644C16.1088 1.42345 16.3238 1.38067 16.5107 1.45806C16.6975 1.53545 16.8193 1.71777 16.8193 1.92V10.56C16.8193 10.8361 16.5955 11.06 16.3193 11.06H7.67934C7.47711 11.06 7.29479 10.9382 7.2174 10.7513C7.14001 10.5645 7.18278 10.3494 7.32578 10.2064L15.9658 1.56644Z" fill="url(#paint0_linear_23674_260133)" stroke="#333333" stroke-linejoin="round"/>
<path d="M62.8799 9.1H63.3799V9.6V48V48.5H62.8799H16.7999H16.2999V48V18.5608V18.3519L16.4485 18.2051L25.5177 9.24432L25.6638 9.1H25.8692H62.8799Z" fill="white" stroke="#333333"/>
<path d="M25.0859 9.24645C25.2289 9.10345 25.444 9.06067 25.6308 9.13806C25.8176 9.21545 25.9395 9.39777 25.9395 9.6V18.24C25.9395 18.5161 25.7156 18.74 25.4395 18.74H16.7994C16.5972 18.74 16.4149 18.6182 16.3375 18.4313C16.2601 18.2445 16.3029 18.0294 16.4459 17.8864L25.0859 9.24645Z" fill="#BDECFF" stroke="#333333" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M57.1201 23.74C57.3963 23.74 57.6201 23.9639 57.6201 24.24C57.6201 24.5161 57.3963 24.74 57.1201 24.74H29.7601C29.484 24.74 29.2601 24.5161 29.2601 24.24C29.2601 23.9639 29.484 23.74 29.7601 23.74H57.1201ZM56.1601 35.3H56.8801C57.1563 35.3 57.3801 35.0761 57.3801 34.8C57.3801 34.5239 57.1563 34.3 56.8801 34.3H56.1601H42.9601H22.0801C21.804 34.3 21.5801 34.5239 21.5801 34.8C21.5801 35.0761 21.804 35.3 22.0801 35.3H42.9601H56.1601ZM57.6201 29.52C57.6201 29.2439 57.3963 29.02 57.1201 29.02H22.0801C21.804 29.02 21.5801 29.2439 21.5801 29.52C21.5801 29.7961 21.804 30.02 22.0801 30.02H57.1201C57.3963 30.02 57.6201 29.7961 57.6201 29.52Z" fill="#333333"/>
<path d="M49.2002 17.52H33.3602" stroke="url(#paint1_linear_23674_260133)" stroke-width="2" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.7591 34.3721L29.3935 39.2564H4.1498C2.06437 39.2564 0.271579 41.1315 0.499599 42.6561L4.56436 67.4328C4.79713 68.9574 6.42415 70.56 8.50958 70.56H64.3404C66.4258 70.56 67.5691 69.2027 67.6878 67.6734L71.4964 35.4188C71.6864 34.4582 70.7173 33.12 69.0119 33.12H36.7281C35.5928 33.12 34.5714 33.5501 33.7591 34.3721Z" fill="url(#paint2_linear_23674_260133)" stroke="#333333"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.7209 42.24V46.1261C30.7209 46.6436 30.5112 47.1121 30.1722 47.4512C29.8331 47.7903 29.3646 48 28.847 48H24.9609V47.3977H28.847C29.1951 47.3977 29.5163 47.2549 29.7438 47.0229C29.9758 46.7909 30.1186 46.4741 30.1186 46.1261V42.24H30.7209Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.9062 43.648V43.5754C46.9062 43.2623 46.7707 42.9718 46.5539 42.7585C46.3326 42.5406 46.0254 42.3954 45.7002 42.3727H30.4426L25.1083 47.7373L26.3865 63.8311C26.4182 64.2078 26.5717 64.5482 26.8111 64.7887C27.0324 65.0111 27.3351 65.1518 27.6783 65.1518H44.3045C44.6478 65.1518 44.9549 65.0111 45.1808 64.7842C45.4202 64.5437 45.5737 64.2078 45.5963 63.8356L46.9062 43.648ZM47.5205 43.5754L46.2061 63.8719C46.1699 64.3939 45.9531 64.8704 45.6144 65.2154C45.2801 65.5512 44.824 65.76 44.309 65.76H27.6783C27.1634 65.76 26.7163 65.5512 26.382 65.2199C26.0433 64.8795 25.8219 64.3984 25.7813 63.881L24.4805 47.5058L30.1942 41.76H45.6189L45.7409 41.7645C46.2151 41.7963 46.6578 42.0051 46.983 42.3228C47.3082 42.6496 47.5205 43.0853 47.5205 43.5754Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_23674_260133" x1="18.1808" y1="1.45762" x2="13.3027" y2="9.34641" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFC671"/>
<stop offset="1" stop-color="#FF6F3D"/>
</linearGradient>
<linearGradient id="paint1_linear_23674_260133" x1="49.2002" y1="17.4809" x2="49.079" y2="19.475" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF8E3D"/>
<stop offset="1" stop-color="#FF6F3D"/>
</linearGradient>
<linearGradient id="paint2_linear_23674_260133" x1="18.2942" y1="20.7558" x2="44.1579" y2="60.4266" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFC671"/>
<stop offset="1" stop-color="#FF6F3D"/>
</linearGradient>
<clipPath id="clip0_23674_260133">
<rect width="72" height="72" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -121,12 +121,15 @@
"RequiredField": "Required field",
"ResetApplication": "Reset application",
"Restore": "Restore",
"RestoreHere": "Restore here",
"Review": "Review",
"Role": "Role",
"SameEmail": "You can't use the same email",
"SaveButton": "Save",
"SaveHereButton": "Save here",
"Search": "Search",
"Select": "Select",
"SelectFile": "Select file",
"SelectAll": "Select all",
"SendButton": "Send",
"Sending": "Sending...",

View File

@ -121,12 +121,15 @@
"RequiredField": "Обязательное поле",
"ResetApplication": "Сбросить приложение",
"Restore": "Восстановить",
"RestoreHere": "Восстановить сюда",
"Review": "Рецензирование",
"Role": "Позиция",
"SameEmail": "Email совпадает с текущим",
"SaveButton": "Сохранить",
"SaveHereButton": "Сохранить сюда",
"Search": "Поиск",
"Select": "Выберите",
"SelectFile": "Выберите файл",
"SelectAll": "Выбрать все",
"SendButton": "Отправить",
"Sending": "Отправка...",

View File

@ -40,6 +40,10 @@ namespace ASC.Web.Api.Controllers
[ApiController]
public class PortalController : ControllerBase
{
private readonly ApiSystemHelper _apiSystemHelper;
private readonly CoreSettings _coreSettings;
private readonly StudioNotifyService _studioNotifyService;
private readonly PermissionContext _permissionContext;
private Tenant Tenant { get { return ApiContext.Tenant; } }
@ -87,11 +91,11 @@ namespace ASC.Web.Api.Controllers
LicenseReader licenseReader,
SetupInfo setupInfo,
DocumentServiceLicense documentServiceLicense,
IHttpClientFactory clientFactory,
ApiSystemHelper apiSystemHelper,
CoreSettings coreSettings,
PermissionContext permissionContext,
ApiSystemHelper apiSystemHelper,
StudioNotifyService studioNotifyService,
IHttpClientFactory clientFactory
StudioNotifyService studioNotifyService
)
{
Log = options.CurrentValue;
@ -117,6 +121,10 @@ namespace ASC.Web.Api.Controllers
StudioNotifyService = studioNotifyService;
TenantExtra = tenantExtra;
ClientFactory = clientFactory;
_apiSystemHelper = apiSystemHelper;
_coreSettings = coreSettings;
_studioNotifyService = studioNotifyService;
_permissionContext = permissionContext;
}
[Read("")]
@ -296,38 +304,35 @@ namespace ASC.Web.Api.Controllers
MobileAppInstallRegistrator.RegisterInstall(currentUser.Email, type);
}
/// <summary>
/// Updates a portal name with a new one specified in the request.
/// </summary>
/// <short>Update a portal name</short>
/// <param name="alias">New portal name</param>
/// <returns>Message about renaming a portal</returns>
///<visible>false</visible>
[Update("portalrename")]
public object UpdatePortalNameFromObject([FromBody] PortalRenameModel model)
public async Task<object> UpdatePortalName(PortalRenameModel model)
{
return UpdatePortalNameAsync(model);
}
[Update("portalrename")]
[Consumes("application/x-www-form-urlencoded")]
public object UpdatePortalNameFromForm([FromForm] PortalRenameModel model)
{
return UpdatePortalNameAsync(model);
}
public async Task<object> UpdatePortalNameAsync(PortalRenameModel model)
{
var enabled = SetupInfo.IsVisibleSettings("PortalRename");
if (!enabled)
throw new SecurityException(Resource.PortalAccessSettingsTariffException);
if (!SetupInfo.IsVisibleSettings(nameof(ManagementType.PortalSecurity)))
{
throw new BillingException(Resource.ErrorNotAllowedOption);
}
if (CoreBaseSettings.Personal)
{
throw new Exception(Resource.ErrorAccessDenied);
}
PermissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
_permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
var alias = model.Alias;
if (string.IsNullOrEmpty(alias)) throw new ArgumentException();
if (string.IsNullOrEmpty(alias)) throw new ArgumentException(nameof(alias));
var tenant = TenantManager.GetCurrentTenant();
var user = UserManager.GetUsers(SecurityContext.CurrentAccount.ID);
var localhost = CoreSettings.BaseDomain == "localhost" || tenant.TenantAlias == "localhost";
var localhost = _coreSettings.BaseDomain == "localhost" || tenant.TenantAlias == "localhost";
var newAlias = alias.ToLowerInvariant();
var oldAlias = tenant.TenantAlias;
@ -335,51 +340,40 @@ namespace ASC.Web.Api.Controllers
if (!string.Equals(newAlias, oldAlias, StringComparison.InvariantCultureIgnoreCase))
{
if (!string.IsNullOrEmpty(ApiSystemHelper.ApiSystemUrl))
if (!string.IsNullOrEmpty(_apiSystemHelper.ApiSystemUrl))
{
await ApiSystemHelper.ValidatePortalNameAsync(newAlias, user.ID);
await _apiSystemHelper.ValidatePortalNameAsync(newAlias, user.ID);
}
else
{
TenantManager.CheckTenantAddress(newAlias.Trim());
}
if (!string.IsNullOrEmpty(ApiSystemHelper.ApiCacheUrl))
if (!string.IsNullOrEmpty(_apiSystemHelper.ApiCacheUrl))
{
await ApiSystemHelper.AddTenantToCacheAsync(newAlias, user.ID);
await _apiSystemHelper.AddTenantToCacheAsync(newAlias, user.ID);
}
tenant.TenantAlias = alias;
tenant = TenantManager.SaveTenant(tenant);
if (!string.IsNullOrEmpty(ApiSystemHelper.ApiCacheUrl))
if (!string.IsNullOrEmpty(_apiSystemHelper.ApiCacheUrl))
{
await ApiSystemHelper.RemoveTenantFromCacheAsync(oldAlias, user.ID);
await _apiSystemHelper.RemoveTenantFromCacheAsync(oldAlias, user.ID);
}
if (!localhost || string.IsNullOrEmpty(tenant.MappedDomain))
{
StudioNotifyService.PortalRenameNotify(tenant, oldVirtualRootPath);
_studioNotifyService.PortalRenameNotify(tenant, oldVirtualRootPath);
}
}
else
{
return null;
return string.Empty;
}
var reference = string.Format("{0}{1}{2}/{3}",
ApiContext.HttpContextAccessor.HttpContext.Request?.Scheme ?? Uri.UriSchemeHttp,
Uri.SchemeDelimiter,
tenant.GetTenantDomain(CoreSettings),
CommonLinkUtility.GetConfirmationUrlRelative(tenant.TenantId, user.Email, ConfirmType.Auth));
return new
{
message = Resource.SuccessfullyPortalRenameMessage,
reference = reference
};
return CommonLinkUtility.GetConfirmationUrl(user.Email, ConfirmType.Auth);
}
}
}

View File

@ -317,15 +317,20 @@ namespace ASC.Api.Settings
[AllowAnonymous]
public SettingsWrapper GetSettings(bool? withpassword)
{
var studioAdminMessageSettings = SettingsManager.Load<StudioAdminMessageSettings>();
var settings = new SettingsWrapper
{
Culture = Tenant.GetCulture().ToString(),
GreetingSettings = Tenant.Name,
Personal = CoreBaseSettings.Personal,
Version = Configuration["version:number"] ?? "",
TenantStatus = TenantManager.GetCurrentTenant().Status
TenantStatus = Tenant.Status,
TenantAlias = Tenant.TenantAlias,
EnableAdmMess = studioAdminMessageSettings.Enable || TenantExtra.IsNotPaid()
};
if (AuthContext.IsAuthenticated)
{
settings.TrustedDomains = Tenant.TrustedDomains;
@ -374,9 +379,7 @@ namespace ASC.Api.Settings
settings.TrustedDomains = Tenant.TrustedDomains;
}
var studioAdminMessageSettings = SettingsManager.Load<StudioAdminMessageSettings>();
settings.EnableAdmMess = studioAdminMessageSettings.Enable || TenantExtra.IsNotPaid();
settings.ThirdpartyEnable = SetupInfo.ThirdPartyAuthEnabled && ProviderManager.IsNotEmpty;

Some files were not shown because too many files have changed in this diff Show More