Merge branch 'release/rc-v1.2.0' of https://github.com/ONLYOFFICE/DocSpace into release/rc-v1.2.0

This commit is contained in:
Timofey Boyko 2022-12-16 20:23:56 +03:00
commit 7bc819dba7
66 changed files with 1519 additions and 1514 deletions

View File

@ -95,7 +95,7 @@
"chunk-size": 10485760,
"url": "/"
},
"viewed-images": [ ".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ico", ".tif", ".tiff", ".webp" ],
"viewed-images": [ ".svg", ".bmp", ".gif", ".jpeg", ".jpg", ".png", ".ico", ".tif", ".tiff", ".webp" ],
"viewed-media": [ ".aac", ".flac", ".m4a", ".mp3", ".oga", ".ogg", ".wav", ".f4v", ".m4v", ".mov", ".mp4", ".ogv", ".webm" ],
"index": [ ".pptx", ".xlsx", ".docx" ],
"oform": {

View File

@ -46,6 +46,7 @@
"FolderRenamed": "The folder '{{folderTitle}}' is renamed to '{{newFoldedTitle}}'",
"Forms": "Forms",
"FormsTemplates": "Forms templates",
"GoToMyRooms": "Go to My rooms",
"GoToPersonal": "Go to Personal",
"GoToShared": "Go to Shared",
"Images": "Images",
@ -68,6 +69,8 @@
"NewPresentation": "New presentation",
"NewRoom": "New room",
"NewSpreadsheet": "New spreadsheet",
"NoAccessRoomTitle": "Sorry, you don't have access to this room",
"NoAccessRoomDescription": "You will be redirected to the My Rooms automatically in 5 seconds.",
"NoFilesHereYet": "No files here yet",
"Open": "Open",
"OpenLocation": "Open location",

View File

@ -29,7 +29,7 @@ export default function withBadges(WrappedComponent) {
isTrashFolder,
} = this.props;
if (isTrashFolder) return;
fetchFileVersions(item.id + "", item.access);
fetchFileVersions(item.id + "", item.security);
setIsVerHistoryPanel(true);
};
@ -82,16 +82,13 @@ export default function withBadges(WrappedComponent) {
t,
theme,
item,
canWebEdit,
isTrashFolder,
isPrivacyFolder,
canConvert,
onFilesClick,
isAdmin,
isDesktopClient,
sectionWidth,
viewAs,
canViewVersionFileHistory,
} = this.props;
const { fileStatus, access } = item;
@ -112,8 +109,6 @@ export default function withBadges(WrappedComponent) {
showNew={showNew}
newItems={newItems}
sectionWidth={sectionWidth}
canWebEdit={canWebEdit}
canConvert={canConvert}
isTrashFolder={isTrashFolder}
isPrivacyFolder={isPrivacyFolder}
isDesktopClient={isDesktopClient}
@ -124,7 +119,6 @@ export default function withBadges(WrappedComponent) {
setConvertDialogVisible={this.setConvertDialogVisible}
onFilesClick={onFilesClick}
viewAs={viewAs}
canViewVersionFileHistory={canViewVersionFileHistory}
/>
);
@ -143,8 +137,6 @@ export default function withBadges(WrappedComponent) {
versionHistoryStore,
dialogsStore,
filesStore,
settingsStore,
accessRightsStore,
},
{ item }
) => {
@ -159,17 +151,10 @@ export default function withBadges(WrappedComponent) {
} = dialogsStore;
const { setIsLoading } = filesStore;
const canWebEdit = settingsStore.canWebEdit(item.fileExst);
const canConvert = settingsStore.canConvert(item.fileExst);
const canViewVersionFileHistory = accessRightsStore.canViewVersionFileHistory(
item
);
return {
theme,
isAdmin: auth.isAdmin,
canWebEdit,
canConvert,
isTrashFolder: isRecycleBinFolder,
isPrivacyFolder,
homepage: config.homepage,
@ -183,7 +168,6 @@ export default function withBadges(WrappedComponent) {
setConvertItem,
isDesktopClient,
setPinAction,
canViewVersionFileHistory,
};
}
)(observer(WithBadges));

View File

@ -201,7 +201,6 @@ export default function withFileActions(WrappedFileItem) {
checked,
dragging,
isFolder,
canWebEdit,
} = this.props;
const { fileExst, access, id } = item;

View File

@ -10,7 +10,7 @@ export default function withQuickButtons(WrappedComponent) {
this.state = {
isLoading: false,
isCanWebEdit: props.canWebEdit(props.item.fileExst),
isCanWebEdit: props.item.viewAccessability?.WebEdit,
};
}
@ -50,15 +50,7 @@ export default function withQuickButtons(WrappedComponent) {
render() {
const { isLoading, isCanWebEdit } = this.state;
const {
t,
theme,
item,
isAdmin,
sectionWidth,
viewAs,
canLockFile,
} = this.props;
const { t, theme, item, isAdmin, sectionWidth, viewAs } = this.props;
const quickButtonsComponent = (
<QuickButtons
@ -72,7 +64,6 @@ export default function withQuickButtons(WrappedComponent) {
isCanWebEdit={isCanWebEdit}
onClickLock={this.onClickLock}
onClickFavorite={this.onClickFavorite}
canLockFile={canLockFile}
/>
);
@ -90,8 +81,7 @@ export default function withQuickButtons(WrappedComponent) {
auth,
filesActionsStore,
dialogsStore,
settingsStore,
accessRightsStore,
treeFoldersStore,
}) => {
const {
@ -101,8 +91,6 @@ export default function withQuickButtons(WrappedComponent) {
} = filesActionsStore;
const { isPersonalRoom } = treeFoldersStore;
const { setSharingPanelVisible } = dialogsStore;
const { canWebEdit } = settingsStore;
const { canLockFile } = accessRightsStore;
return {
theme: auth.settingsStore.theme,
@ -111,8 +99,7 @@ export default function withQuickButtons(WrappedComponent) {
setFavoriteAction,
onSelectItem,
setSharingPanelVisible,
canWebEdit,
canLockFile,
isPersonalRoom,
};
}

View File

@ -58,11 +58,9 @@ const Badges = ({
newItems,
sectionWidth,
item,
canWebEdit,
isTrashFolder,
isPrivacyFolder,
isDesktopClient,
canConvert,
accessToEdit,
showNew,
onFilesClick,
@ -71,7 +69,6 @@ const Badges = ({
setConvertDialogVisible,
viewAs,
onUnpinClick,
canViewVersionFileHistory,
}) => {
const {
id,
@ -143,7 +140,7 @@ const Badges = ({
"data-id": id,
};
const onShowVersionHistoryProp = canViewVersionFileHistory
const onShowVersionHistoryProp = item.security?.ReadHistory
? { onClick: onShowVersionHistory }
: {};
@ -161,7 +158,7 @@ const Badges = ({
title={isForm ? t("Common:FillFormButton") : t("Common:EditButton")}
/>
)}
{canConvert && !isTrashFolder && (
{item.viewAccessability?.Convert && !isTrashFolder && (
<ColorTheme
themeId={ThemeType.IconButton}
onClick={setConvertDialogVisible}

View File

@ -9,6 +9,7 @@ import {
size,
} from "@docspace/components/utils/device";
import { isMobile, isMobileOnly } from "react-device-detect";
import { classNames } from "@docspace/components/utils/classNames";
const EmptyPageStyles = css`
padding: 44px 0px 64px 0px;
@ -122,6 +123,10 @@ const EmptyFolderWrapper = styled.div`
}
`}
}
.empty-folder_room-not-found {
margin-top: 70px;
}
`;
const EmptyFoldersContainer = (props) => {
@ -138,6 +143,7 @@ const EmptyFoldersContainer = (props) => {
isEmptyPage,
sectionWidth,
isEmptyFolderContainer,
className,
} = props;
return (
@ -148,7 +154,7 @@ const EmptyFoldersContainer = (props) => {
>
<EmptyScreenContainer
sectionWidth={sectionWidth}
className="empty-folder_container"
className={classNames("empty-folder_container", className)}
style={style}
imageStyle={imageStyle}
imageSrc={imageSrc}

View File

@ -166,6 +166,7 @@ export default inject(
access,
id: folderId,
roomType,
security,
} = selectedFolderStore;
let id;
@ -176,11 +177,11 @@ export default inject(
const isRooms = !!roomType;
const { canCreateFiles, canInviteUserInRoom } = accessRightsStore;
const { canCreateFiles } = accessRightsStore;
const { onClickInviteUsers } = contextOptionsStore;
const canInviteUsers = isRooms && canInviteUserInRoom({ access }); // skip sub-folders
const canInviteUsers = isRooms && security?.EditAccess; // skip sub-folders
return {
fetchFiles,

View File

@ -0,0 +1,105 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import EmptyContainer from "./EmptyContainer";
import Link from "@docspace/components/link";
import RoomsFilter from "@docspace/common/api/rooms/filter";
import { combineUrl } from "@docspace/common/utils";
import { getCategoryUrl } from "SRC_DIR/helpers/utils";
import { AppServerConfig } from "@docspace/common/constants";
import history from "@docspace/common/history";
import config from "PACKAGE_FILE";
const RoomNoAccessContainer = (props) => {
const {
t,
setIsLoading,
linkStyles,
fetchRooms,
setAlreadyFetchingRooms,
categoryType,
isEmptyPage,
sectionWidth,
} = props;
const descriptionRoomNoAccess = t("NoAccessRoomDescription");
const titleRoomNoAccess = t("NoAccessRoomTitle");
React.useEffect(() => {
const timer = setTimeout(onGoToShared, 5000);
return () => clearTimeout(timer);
}, []);
const onGoToShared = () => {
setIsLoading(true);
setAlreadyFetchingRooms(true);
fetchRooms(null, null)
.then(() => {
const filter = RoomsFilter.getDefault();
const filterParamsStr = filter.toUrlParams();
const url = getCategoryUrl(categoryType, filter.folder);
const pathname = `${url}?${filterParamsStr}`;
history.push(
combineUrl(AppServerConfig.proxyURL, config.homepage, pathname)
);
})
.finally(() => {
setIsLoading(false);
});
};
const goToButtons = (
<div className="empty-folder_container-links">
<img
className="empty-folder_container-image"
src="images/empty-folder-image.svg"
onClick={onGoToShared}
alt="folder_icon"
/>
<Link onClick={onGoToShared} {...linkStyles}>
{t("GoToMyRooms")}
</Link>
</div>
);
const propsRoomNotFoundOrMoved = {
headerText: titleRoomNoAccess,
descriptionText: descriptionRoomNoAccess,
imageSrc: "static/images/manage.access.rights.react.svg",
buttons: goToButtons,
};
return (
<EmptyContainer
isEmptyPage={isEmptyPage}
sectionWidth={sectionWidth}
imageStyle={{ marginRight: "20px" }}
className="empty-folder_room-not-found"
{...propsRoomNotFoundOrMoved}
/>
);
};
export default inject(({ filesStore }) => {
const {
setIsLoading,
fetchRooms,
categoryType,
setAlreadyFetchingRooms,
isEmptyPage,
} = filesStore;
return {
setIsLoading,
fetchRooms,
categoryType,
setAlreadyFetchingRooms,
isEmptyPage,
};
})(withTranslation(["Files"])(observer(RoomNoAccessContainer)));

View File

@ -6,6 +6,7 @@ import EmptyFolderContainer from "./EmptyFolderContainer";
import { FileAction } from "@docspace/common/constants";
import { isMobile } from "react-device-detect";
import { Events } from "@docspace/common/constants";
import RoomNoAccessContainer from "./RoomNoAccessContainer";
const linkStyles = {
isHovered: true,
@ -21,6 +22,7 @@ const EmptyContainer = ({
theme,
setCreateRoomDialogVisible,
sectionWidth,
isRoomNotFoundOrMoved,
}) => {
linkStyles.color = theme.filesEmptyContainer.linkColor;
@ -43,6 +45,15 @@ const EmptyContainer = ({
window.dispatchEvent(event);
};
if (isRoomNotFoundOrMoved) {
return (
<RoomNoAccessContainer
linkStyles={linkStyles}
sectionWidth={sectionWidth}
/>
);
}
return isFiltered ? (
<EmptyFilterContainer linkStyles={linkStyles} />
) : parentId === 0 ? (
@ -69,7 +80,7 @@ export default inject(
treeFoldersStore,
selectedFolderStore,
}) => {
const { filter, roomsFilter } = filesStore;
const { filter, roomsFilter, isErrorRoomNotAvailable } = filesStore;
const {
authorType,
@ -113,12 +124,18 @@ export default inject(
searchInContent) &&
!(isPrivacyFolder && isMobile);
const isRoomNotFoundOrMoved =
isFiltered === null &&
selectedFolderStore.parentId === null &&
isErrorRoomNotAvailable;
return {
theme: auth.settingsStore.theme,
isFiltered,
setCreateRoomDialogVisible,
parentId: selectedFolderStore.parentId,
isRoomNotFoundOrMoved,
};
}
)(observer(EmptyContainer));

View File

@ -139,8 +139,19 @@ const CreateEvent = ({
})
.then(() => editCompleteAction(item, type))
.catch((err) => {
if (err.indexOf("password") == -1) {
toastr.error(err, t("Common:Warning"));
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
if (errorMessage.indexOf("password") == -1) {
toastr.error(errorMessage, t("Common:Warning"));
return;
}

View File

@ -22,11 +22,10 @@ const QuickButtons = (props) => {
onClickFavorite,
viewAs,
isCanWebEdit,
canLockFile,
} = props;
const { id, locked, fileStatus, title, fileExst, access, folderType } = item;
const canLockFileAbility = canLockFile(item);
const { id, locked, fileStatus, title, fileExst, security } = item;
const canLockFileAbility = security?.Lock;
const isFavorite =
(fileStatus & FileStatus.IsFavorite) === FileStatus.IsFavorite;

View File

@ -92,8 +92,19 @@ const ConvertPasswordDialogComponent = (props) => {
onClose();
})
.catch((err) => {
if (err.indexOf("password") == -1) {
toastr.error(err, t("Common:Warning"));
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
if (errorMessage.indexOf("password") == -1) {
toastr.error(errorMessage, t("Common:Warning"));
return;
}
@ -118,8 +129,18 @@ const ConvertPasswordDialogComponent = (props) => {
editCompleteAction(fileInfo);
})
.catch((err) => {
if (err.indexOf("password") == -1) {
toastr.error(err, t("Common:Warning"));
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
if (errorMessage.indexOf("password") == -1) {
toastr.error(errorMessage, t("Common:Warning"));
return;
}

View File

@ -359,12 +359,7 @@ export default inject(
name = splitted[0];
}
const { personal, theme } = auth.settingsStore;
const {
canViewedDocs,
isMediaOrImage,
getIconSrc,
isArchive,
} = settingsStore;
const { getIconSrc, isArchive } = settingsStore;
const {
uploaded,
primaryProgressDataStore,
@ -382,7 +377,8 @@ export default inject(
setCurrentItem,
} = mediaViewerDataStore;
const { loadingFile: file } = primaryProgressDataStore;
const isMedia = isMediaOrImage(ext);
const isMedia =
item.viewAccessability?.ImageView || item.viewAccessability?.MediaView;
const isMediaActive =
playlist.findIndex((el) => el.fileId === item.fileId) !== -1;
@ -395,7 +391,8 @@ export default inject(
? loadingFile.percent
: null;
const downloadInCurrentTab = isArchive(ext) || !canViewedDocs(ext);
const downloadInCurrentTab =
isArchive(ext) || !item.viewAccessability?.WebView;
return {
isPersonal: personal,

View File

@ -71,8 +71,18 @@ class ConfirmRoute extends React.Component {
}
})
.catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
history.push(
combineUrl(AppServerConfig.proxyURL, path, `/error=${error}`)
combineUrl(AppServerConfig.proxyURL, path, `/error=${errorMessage}`)
);
});
}

View File

@ -26,10 +26,24 @@ class ActivateEmail extends React.PureComponent {
)
);
})
.catch((e) => {
.catch((error) => {
// console.log('activate email error', e);
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
tryRedirectTo(
combineUrl(AppServerConfig.proxyURL, `/login/error?message=${e}`)
combineUrl(
AppServerConfig.proxyURL,
`/login/error?message=${errorMessage}`
)
);
})
);

View File

@ -23,9 +23,22 @@ class ChangeEmail extends React.PureComponent {
)
);
})
.catch((e) => {
.catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.log("change client email error", e);
tryRedirectTo(combineUrl(AppServerConfig.proxyURL, `/error=${e}`));
tryRedirectTo(
combineUrl(AppServerConfig.proxyURL, `/error=${errorMessage}`)
);
});
}
}

View File

@ -67,7 +67,18 @@ const ChangePasswordForm = (props) => {
tryRedirectTo(defaultPage);
})
.catch((error) => {
toastr.error(t(`${error}`));
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
toastr.error(t(`${errorMessage}`));
setIsLoading(false);
});
};

View File

@ -339,8 +339,19 @@ const Confirm = (props) => {
createConfirmUser(personalData, loginData, headerKey)
.then(() => window.location.replace(defaultPage))
.catch((error) => {
console.error("confirm error", error);
setEmailErrorText(error);
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error("confirm error", errorMessage);
setEmailErrorText(errorMessage);
setEmailValid(false);
setIsLoading(false);
});

View File

@ -120,8 +120,18 @@ const TfaActivationForm = withLoader((props) => {
const url = await loginWithCodeAndCookie(code, linkData.confirmHeader);
history.push("/");
}
} catch (e) {
setError(e);
} catch (err) {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
setError(errorMessage);
toastr.error(e);
}
setIsLoading(false);

View File

@ -34,11 +34,22 @@ export default function withLoader(WrappedComponent) {
axios
.all([getSettings(), getPortalPasswordSettings(confirmHeader)])
.catch((error) => {
console.error(error);
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error(errorMessage);
history.push(
combineUrl(
AppServerConfig.proxyURL,
`/login/error?message=${error}`
`/login/error?message=${errorMessage}`
)
);
});
@ -48,11 +59,21 @@ export default function withLoader(WrappedComponent) {
useEffect(() => {
if (type === "LinkInvite") {
axios.all([getAuthProviders(), getCapabilities()]).catch((error) => {
console.error(error);
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error(errorMessage);
history.push(
combineUrl(
AppServerConfig.proxyURL,
`/login/error?message=${error}`
`/login/error?message=${errorMessage}`
)
);
});

View File

@ -49,16 +49,12 @@ class MembersHelper {
};
};
getOptionsByRoomType = (
roomType,
canChangeUserRole = false,
canDeleteUser = false
) => {
getOptionsByRoomType = (roomType, canChangeUserRole = false) => {
if (!roomType) return;
const options = this.getOptions();
const deleteOption = canDeleteUser
const deleteOption = canChangeUserRole
? [
{ key: "s2", isSeparator: true },
{
@ -69,50 +65,41 @@ class MembersHelper {
]
: [];
let availableOptions = [];
switch (roomType) {
case RoomsType.FillingFormsRoom:
if (canChangeUserRole)
availableOptions = [
options.roomAdmin,
options.formFiller,
options.viewer,
];
return [...availableOptions, ...deleteOption];
return [
options.roomAdmin,
options.formFiller,
options.viewer,
...deleteOption,
];
case RoomsType.EditingRoom:
if (canChangeUserRole)
availableOptions = [
options.roomAdmin,
options.editor,
options.viewer,
];
return [...availableOptions, ...deleteOption];
return [
options.roomAdmin,
options.editor,
options.viewer,
...deleteOption,
];
case RoomsType.ReviewRoom:
if (canChangeUserRole)
availableOptions = [
options.roomAdmin,
options.reviewer,
options.commentator,
options.viewer,
];
return [...availableOptions, ...deleteOption];
return [
options.roomAdmin,
options.reviewer,
options.commentator,
options.viewer,
...deleteOption,
];
case RoomsType.ReadOnlyRoom:
if (canChangeUserRole)
availableOptions = [options.roomAdmin, options.viewer];
return [...availableOptions, ...deleteOption];
return [options.roomAdmin, options.viewer, ...deleteOption];
case RoomsType.CustomRoom:
if (canChangeUserRole)
availableOptions = [
options.roomAdmin,
options.editor,
options.formFiller,
options.reviewer,
options.commentator,
options.viewer,
];
return [...availableOptions, ...deleteOption];
return [
options.roomAdmin,
options.editor,
options.formFiller,
options.reviewer,
options.commentator,
options.viewer,
...deleteOption,
];
}
};

View File

@ -123,7 +123,11 @@ const InfoPanelBodyContent = ({
const newSelectionParentRoom = await getRoomInfo(currentFolderRoomId);
if (storeRoomId === newSelectionParentRoom.id) return;
setSelectionParentRoom(normalizeSelection(newSelectionParentRoom));
if (
newSelectionParentRoom.parentId === newSelectionParentRoom.rootFolderId
) {
setSelectionParentRoom(null);
} else setSelectionParentRoom(normalizeSelection(newSelectionParentRoom));
}, [selectedFolder]);
//////////////////////////////////////////////////////////

View File

@ -13,21 +13,17 @@ const CommentEditor = ({
setSelection,
fetchFileVersions,
updateCommentVersion,
canChangeVersionFileHistory,
setVerHistoryFileId,
setVerHistoryFileAccess,
}) => {
const { id, comment, version, access, folderType } = item;
const changeVersionHistoryAbility = canChangeVersionFileHistory({
access,
folderType,
editing,
});
setVerHistoryFileId,
setVerHistoryFileSecurity,
}) => {
const { id, comment, version, security } = item;
const changeVersionHistoryAbility = !editing && security?.EditHistory;
useEffect(() => {
setVerHistoryFileId(id);
setVerHistoryFileAccess(access);
setVerHistoryFileSecurity(security);
}, []);
const [isEdit, setIsEdit] = useState(false);
@ -44,7 +40,7 @@ const CommentEditor = ({
const onSave = async () => {
setIsLoading(true);
await fetchFileVersions(id, access).catch((err) => {
await fetchFileVersions(id, security).catch((err) => {
toastr.error(err);
setIsLoading(false);
});
@ -116,7 +112,7 @@ const CommentEditor = ({
);
};
export default inject(({ auth, versionHistoryStore, accessRightsStore }) => {
export default inject(({ auth, versionHistoryStore }) => {
const { setSelection } = auth.infoPanelStore;
const {
@ -126,19 +122,18 @@ export default inject(({ auth, versionHistoryStore, accessRightsStore }) => {
isEditing,
fileId,
setVerHistoryFileId,
setVerHistoryFileAccess,
setVerHistoryFileSecurity,
} = versionHistoryStore;
const { canChangeVersionFileHistory } = accessRightsStore;
const editing = isEditingVersion || isEditing;
return {
setSelection,
fetchFileVersions,
updateCommentVersion,
canChangeVersionFileHistory,
editing,
setVerHistoryFileId,
setVerHistoryFileAccess,
setVerHistoryFileSecurity,
};
})(CommentEditor);

View File

@ -13,10 +13,6 @@ const User = ({
updateRoomMemberRole,
selectionParentRoom,
setSelectionParentRoom,
canChangeUserRoleInRoom,
canDeleteUserInRoom,
rootFolderType,
access,
}) => {
if (!selectionParentRoom) return null;
if (!user.displayName && !user.email) return null;
@ -24,26 +20,11 @@ const User = ({
const [userIsRemoved, setUserIsRemoved] = useState(false);
if (userIsRemoved) return null;
const canChangeUserRole =
user &&
canChangeUserRoleInRoom({
access,
rootFolderType,
currentUserInList: { id: user.id, access: user.access },
});
const canDeleteUser =
user &&
canDeleteUserInRoom({
access,
rootFolderType,
currentUserInList: { id: user.id, access: user.access },
});
const canChangeUserRole = user.canEditAccess;
const fullRoomRoleOptions = membersHelper.getOptionsByRoomType(
selectionParentRoom.roomType,
canChangeUserRole,
canDeleteUser
canChangeUserRole
);
const userRole = membersHelper.getOptionByUserAccess(user.access);
@ -103,7 +84,7 @@ const User = ({
{userRole && userRoleOptions && (
<div className="role-wrapper">
{canChangeUserRole || canDeleteUser ? (
{canChangeUserRole ? (
<ComboBox
className="role-combobox"
selectedOption={userRole}

View File

@ -30,29 +30,25 @@ const Members = ({
getRoomMembers,
updateRoomMemberRole,
setView,
roomsView,
resendEmailInvitations,
setInvitePanelOptions,
canDeleteUserInRoom,
changeUserType,
canInviteUserInRoom,
canChangeUserRoleInRoom,
}) => {
const membersHelper = new MembersHelper({ t });
const [members, setMembers] = useState(null);
const [showLoader, setShowLoader] = useState(false);
const { access, rootFolderType } = selection;
const canInviteUserInRoomAbility = canInviteUserInRoom({
access,
rootFolderType,
});
const security = selectionParentRoom ? selectionParentRoom.security : {};
const canInviteUserInRoomAbility = security?.EditAccess;
const fetchMembers = async (roomId) => {
let timerId;
if (members) timerId = setTimeout(() => setShowLoader(true), 1000);
let data = await getRoomMembers(roomId);
data = data.filter((m) => m.sharedTo.email || m.sharedTo.displayName);
clearTimeout(timerId);
@ -61,6 +57,7 @@ const Members = ({
data.map((fetchedMember) => {
const member = {
access: fetchedMember.access,
canEditAccess: fetchedMember.canEditAccess,
...fetchedMember.sharedTo,
};
if (member.activationStatus !== 2) inRoomMembers.push(member);
@ -98,6 +95,8 @@ const Members = ({
...selection,
members: fetchedMembers,
});
if (roomsView === "info_members" && !selection?.security?.Read)
setView("info_details");
}, [selection]);
useEffect(async () => {
@ -162,8 +161,7 @@ const Members = ({
<StyledUserList>
{Object.values(members.inRoom).map((user) => (
<User
access={access}
rootFolderType={rootFolderType}
security={security}
key={user.id}
t={t}
user={user}
@ -174,8 +172,6 @@ const Members = ({
roomType={selectionParentRoom.roomType}
selectionParentRoom={selectionParentRoom}
setSelectionParentRoom={setSelectionParentRoom}
canChangeUserRoleInRoom={canChangeUserRoleInRoom}
canDeleteUserInRoom={canDeleteUserInRoom}
/>
))}
</StyledUserList>
@ -199,8 +195,7 @@ const Members = ({
<StyledUserList>
{Object.values(members.expected).map((user) => (
<User
access={access}
rootFolderType={rootFolderType}
security={security}
isExpect
key={user.id}
t={t}
@ -212,8 +207,6 @@ const Members = ({
roomType={selectionParentRoom.roomType}
selectionParentRoom={selectionParentRoom}
setSelectionParentRoom={setSelectionParentRoom}
canDeleteUserInRoom={canDeleteUserInRoom}
canChangeUserRoleInRoom={canChangeUserRoleInRoom}
/>
))}
</StyledUserList>
@ -226,7 +219,9 @@ export default inject(
const {
setIsMobileHidden,
selectionParentRoom,
setSelectionParentRoom,
setView,
roomsView,
updateRoomMembers,
@ -239,14 +234,10 @@ export default inject(
} = filesStore;
const { isOwner, isAdmin, id: selfId } = auth.userStore.user;
const { setInvitePanelOptions } = dialogsStore;
const { changeType: changeUserType } = peopleStore;
const {
canInviteUserInRoom,
canChangeUserRoleInRoom,
canDeleteUserInRoom,
} = accessRightsStore;
return {
setView,
roomsView,
setIsMobileHidden,
selectionParentRoom,
setSelectionParentRoom,
@ -263,11 +254,6 @@ export default inject(
setInvitePanelOptions,
resendEmailInvitations,
changeUserType,
canInviteUserInRoom,
canChangeUserRoleInRoom,
canDeleteUserInRoom,
};
}
)(

View File

@ -18,7 +18,6 @@ import { ColorTheme, ThemeType } from "@docspace/common/components/ColorTheme";
import { StyledInfoPanelHeader } from "./styles/common";
import { FolderType } from "@docspace/common/constants";
import { getArchiveRoomRoleActions } from "@docspace/common/utils/actions";
const InfoPanelHeaderContent = (props) => {
const {
@ -33,7 +32,7 @@ const InfoPanelHeaderContent = (props) => {
getIsAccounts,
isRootFolder,
rootFolderType,
canViewUsers,
selectionParentRoom,
} = props;
const isRooms = getIsRooms();
@ -73,9 +72,12 @@ const InfoPanelHeaderContent = (props) => {
content: null,
},
];
const selectionRoomRights = selectionParentRoom
? selectionParentRoom.security?.Read
: selection?.security?.Read;
const roomsSubmenu = isArchiveRoot
? canViewUsers(selection)
? selectionRoomRights
? [{ ...submenuData[0] }, { ...submenuData[2] }]
: [{ ...submenuData[2] }]
: [...submenuData];
@ -144,9 +146,9 @@ export default inject(({ auth, selectedFolderStore, accessRightsStore }) => {
getIsRooms,
getIsGallery,
getIsAccounts,
selectionParentRoom,
} = auth.infoPanelStore;
const { isRootFolder, rootFolderType } = selectedFolderStore;
const { canViewUsers } = accessRightsStore;
return {
selection,
@ -161,7 +163,8 @@ export default inject(({ auth, selectedFolderStore, accessRightsStore }) => {
isRootFolder,
rootFolderType,
canViewUsers,
selectionParentRoom,
};
})(
withTranslation(["Common", "InfoPanel"])(

View File

@ -225,7 +225,7 @@ const SimpleFilesRow = (props) => {
const [isDragOver, setIsDragOver] = React.useState(false);
const withAccess = isAdmin || item.access === 0;
const withAccess = item.security?.Lock;
const isSmallContainer = sectionWidth <= 500;
const element = (

View File

@ -46,7 +46,7 @@ class PureHome extends React.Component {
setFirstLoad,
setToPreviewFile,
playlist,
isMediaOrImage,
getFileInfo,
gallerySelected,
setIsUpdatingRowItem,
@ -68,7 +68,9 @@ class PureHome extends React.Component {
setTimeout(() => {
getFileInfo(fileId)
.then((data) => {
const canOpenPlayer = isMediaOrImage(data.fileExst);
const canOpenPlayer =
data.viewAccessability.ImageView ||
data.viewAccessability.MediaView;
const file = { ...data, canOpenPlayer };
setToPreviewFile(file, true);
})
@ -465,6 +467,7 @@ class PureHome extends React.Component {
isHeaderVisible,
isPrivacyFolder,
isRecycleBinFolder,
isErrorRoomNotAvailable,
primaryProgressDataVisible,
primaryProgressDataPercent,
@ -525,13 +528,15 @@ class PureHome extends React.Component {
onOpenUploadPanel={this.showUploadPanel}
firstLoad={firstLoad}
>
<Section.SectionHeader>
{isFrame ? (
showTitle && <SectionHeaderContent />
) : (
<SectionHeaderContent />
)}
</Section.SectionHeader>
{!isErrorRoomNotAvailable && (
<Section.SectionHeader>
{isFrame ? (
showTitle && <SectionHeaderContent />
) : (
<SectionHeaderContent />
)}
</Section.SectionHeader>
)}
{!isEmptyPage && (
<Section.SectionFilter>
@ -619,6 +624,7 @@ export default inject(
isEmptyPage,
disableDrag,
isErrorRoomNotAvailable,
} = filesStore;
const { gallerySelected } = oformsStore;
@ -721,7 +727,7 @@ export default inject(
itemsSelectionLength,
itemsSelectionTitle,
isErrorRoomNotAvailable,
isRoomsFolder,
isArchiveFolder,
@ -744,7 +750,7 @@ export default inject(
personal,
setToPreviewFile,
playlist,
isMediaOrImage: settingsStore.isMediaOrImage,
getFileInfo,
gallerySelected,
setIsUpdatingRowItem,

View File

@ -114,8 +114,19 @@ const PortalRenaming = (props) => {
setPortalRename(portalName)
.then(() => toastr.success(t("SuccessfullySavePortalNameMessage")))
.catch((error) => {
setErrorValue(error);
saveToSessionStorage("errorValue", error);
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
setErrorValue(errorMessage);
saveToSessionStorage("errorValue", errorMessage);
})
.finally(() => setIsLoadingPortalNameSave(false));

View File

@ -50,11 +50,22 @@ class PreparationPortal extends React.Component {
}
}
})
.catch((err) =>
.catch((err) => {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
this.setState({
errorMessage: err,
})
);
errorMessage: errorMessage,
});
});
}
componentWillUnmount() {
clearInterval(this.timerId);

View File

@ -220,44 +220,34 @@ const VersionRow = (props) => {
);
};
export default inject(
({ auth, versionHistoryStore, accessRightsStore, selectedFolderStore }) => {
const { user } = auth.userStore;
const { culture, isTabletView } = auth.settingsStore;
const language = (user && user.cultureName) || culture || "en";
export default inject(({ auth, versionHistoryStore, selectedFolderStore }) => {
const { user } = auth.userStore;
const { culture, isTabletView } = auth.settingsStore;
const language = (user && user.cultureName) || culture || "en";
const {
markAsVersion,
restoreVersion,
updateCommentVersion,
isEditing,
isEditingVersion,
fileAccess,
} = versionHistoryStore;
const {
markAsVersion,
restoreVersion,
updateCommentVersion,
isEditing,
isEditingVersion,
fileSecurity,
} = versionHistoryStore;
const { rootFolderType } = selectedFolderStore;
const isEdit = isEditingVersion || isEditing;
const canChangeVersionFileHistory = !isEdit && fileSecurity?.EditHistory;
const isEdit = isEditingVersion || isEditing;
const canChangeVersionFileHistory = accessRightsStore.canChangeVersionFileHistory(
{
access: fileAccess,
rootFolderType,
editing: isEdit,
}
);
return {
theme: auth.settingsStore.theme,
culture: language,
isTabletView,
markAsVersion,
restoreVersion,
updateCommentVersion,
isEditing: isEdit,
canChangeVersionFileHistory,
};
}
)(
return {
theme: auth.settingsStore.theme,
culture: language,
isTabletView,
markAsVersion,
restoreVersion,
updateCommentVersion,
isEditing: isEdit,
canChangeVersionFileHistory,
};
})(
withRouter(
withTranslation(["VersionHistory", "Common", "Translations"])(
observer(VersionRow)

View File

@ -24,15 +24,15 @@ class SectionBodyContent extends React.Component {
const fileId = match.params.fileId || this.props.fileId;
if (fileId && fileId !== this.props.fileId) {
this.getFileVersions(fileId, fileAccess);
this.getFileVersions(fileId, this.props.fileSecurity);
setFirstLoad(false);
}
}
getFileVersions = (fileId, fileAccess) => {
getFileVersions = (fileId, fileSecurity) => {
const { fetchFileVersions, setIsLoading } = this.props;
setIsLoading(true);
fetchFileVersions(fileId, fileAccess).then(() => setIsLoading(false));
fetchFileVersions(fileId, fileSecurity).then(() => setIsLoading(false));
};
onSetRestoreProcess = (restoring) => {
@ -142,7 +142,7 @@ export default inject(({ auth, filesStore, versionHistoryStore }) => {
versions,
fetchFileVersions,
fileId,
fileAccess,
fileSecurity,
} = versionHistoryStore;
return {
@ -150,7 +150,7 @@ export default inject(({ auth, filesStore, versionHistoryStore }) => {
isLoading,
versions,
fileId,
fileAccess,
fileSecurity,
setFirstLoad,
setIsLoading,
fetchFileVersions,

View File

@ -138,10 +138,20 @@ class Body extends Component {
});
setIsWizardLoaded(true);
})
.catch((e) => {
console.error(e);
.catch((error) => {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
console.error(errorMessage);
this.setState({
errorInitWizard: e,
errorInitWizard: errorMessage,
});
});
}
@ -246,13 +256,24 @@ class Body extends Component {
.then(() =>
history.push(combineUrl(AppServerConfig.proxyURL, "/login"))
)
.catch((e) =>
.catch((error) => {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
this.setState({
errorLoading: true,
sending: false,
errorMessage: e,
})
);
errorMessage: errorMessage,
});
});
} else {
this.setState({ visibleModal: true });
}
@ -382,13 +403,23 @@ class Body extends Component {
let fd = new FormData();
fd.append("files", file);
setLicense(wizardToken, fd).catch((e) =>
setLicense(wizardToken, fd).catch((error) => {
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
this.setState({
errorLoading: true,
errorMessage: e,
errorMessage: errorMessage,
hasErrorLicense: true,
})
);
});
});
};
render() {

View File

@ -6,12 +6,6 @@ import {
FolderType,
ShareAccessRights,
} from "@docspace/common/constants";
import {
getFileRoleActions,
getRoomRoleActions,
getArchiveRoomRoleActions,
getArchiveFileRoleActions,
} from "@docspace/common/utils/actions";
class AccessRightsStore {
authStore = null;
@ -25,281 +19,20 @@ class AccessRightsStore {
makeAutoObservable(this);
}
canInviteUserInRoom(room) {
const { access, rootFolderType } = room;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).inviteUsers;
return getRoomRoleActions(access).inviteUsers;
}
canChangeUserRoleInRoom = (room) => {
const { access, rootFolderType, currentUserInList } = room;
const { userStore } = this.authStore;
const { user } = userStore;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).changeUserRole;
const isMyProfile = user.id === currentUserInList.id;
const isOwnerRoleRoom =
currentUserInList.access === ShareAccessRights.FullAccess;
if (isMyProfile || isOwnerRoleRoom) return false;
return getRoomRoleActions(access).changeUserRole;
};
canDeleteUserInRoom = (room) => {
const { access, currentUserInList, rootFolderType } = room;
const { userStore } = this.authStore;
const { user } = userStore;
const isMyProfile = user.id === currentUserInList.id;
const isOwnerRoleRoom =
currentUserInList.access === ShareAccessRights.FullAccess;
if (isMyProfile || isOwnerRoleRoom) return false;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).deleteUsers;
return getRoomRoleActions(access).deleteUsers;
};
canLockFile = (file) => {
const { rootFolderType, access } = file;
if (rootFolderType === FolderType.USER) return false;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).block;
if (rootFolderType === FolderType.TRASH) return false;
return getFileRoleActions(access).block;
};
canChangeVersionFileHistory = (file) => {
const { rootFolderType, editing, providerKey, access } = file;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).changeVersionHistory;
if (
rootFolderType === FolderType.TRASH ||
// rootFolderType === FolderType.Privacy ||
editing ||
providerKey
)
return false;
return getFileRoleActions(access).changeVersionHistory;
};
canViewVersionFileHistory = (file) => {
const { rootFolderType, access, providerKey } = file;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).viewVersionHistory;
if (
rootFolderType === FolderType.TRASH ||
// rootFolderType === FolderType.Privacy ||
providerKey
)
return false;
return getFileRoleActions(access).viewVersionHistory;
};
canEditFile = (file) => {
const { rootFolderType, access } = file;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).edit;
if (
rootFolderType === FolderType.TRASH
// || rootFolderType === FolderType.Privacy
)
return false;
return getFileRoleActions(access).edit;
};
canRenameItem = (item = {}) => {
const { rootFolderType, access, isFile } = item;
const { isDesktopClient } = this.authStore.settingsStore;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).rename;
if (
rootFolderType === FolderType.TRASH
// ||
// (!isFile && rootFolderType === FolderType.Privacy && !isDesktopClient)
)
return false;
return getFileRoleActions(access).rename;
};
canFillForm = (file) => {
const { rootFolderType, access } = file;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).fillForm;
if (rootFolderType === FolderType.TRASH) return false;
return getFileRoleActions(access).fillForm;
};
canMakeForm = (item) => {
const { rootFolderType, access } = item;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).saveAsForm;
if (
rootFolderType === FolderType.TRASH ||
// rootFolderType === FolderType.Privacy ||
rootFolderType === FolderType.Favorites ||
rootFolderType === FolderType.Recent
)
return false;
return getFileRoleActions(access).saveAsForm;
};
canArchiveRoom = (room) => {
const { archive } = getRoomRoleActions(room.access);
return archive;
};
canRemoveRoom = (room) => {
const { access, rootFolderType } = room;
if (rootFolderType !== FolderType.Archive)
return getRoomRoleActions(access).delete;
return getArchiveRoomRoleActions(access).delete;
};
canViewRoomInfo = (room) => {
const { access, rootFolderType } = room;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).viewInfo;
return getRoomRoleActions(access).viewInfo;
};
canPinRoom = (room) => {
const { access, rootFolderType } = room;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).canPin;
return getRoomRoleActions(access).canPin;
};
canEditRoom = (room) => {
const { access, rootFolderType } = room;
if (rootFolderType === FolderType.Archive)
return getArchiveRoomRoleActions(access).edit;
return getRoomRoleActions(access).edit;
};
get canCreateFiles() {
const { access, rootFolderType } = this.selectedFolderStore;
const { security } = this.selectedFolderStore;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).create;
const { create } = getFileRoleActions(access);
return create;
return security?.Create;
}
canMoveItems = (item) => {
const { rootFolderType, access, editing: fileEditing, providerKey } = item;
const { editing: fileEditing, security, rootFolderType } = item;
if (rootFolderType === FolderType.Archive) {
const { moveSelf, moveAlien } = getArchiveFileRoleActions(access);
if (rootFolderType === FolderType.TRASH || fileEditing) return false;
return moveSelf || moveAlien;
}
if (
rootFolderType === FolderType.TRASH ||
rootFolderType === FolderType.Favorites ||
rootFolderType === FolderType.Recent ||
// rootFolderType === FolderType.Privacy ||
providerKey ||
fileEditing
)
return false;
const { moveSelf, moveAlien } = getFileRoleActions(access);
return moveSelf || moveAlien;
return security?.Move;
};
canDeleteItems = (item) => {
const { rootFolderType, access, editing: fileEditing } = item;
if (rootFolderType === FolderType.Archive) {
const { deleteSelf, deleteAlien } = getArchiveFileRoleActions(access);
return deleteSelf || deleteAlien;
}
if (
rootFolderType === FolderType.Favorites ||
rootFolderType === FolderType.Recent ||
// rootFolderType === FolderType.Privacy ||
fileEditing
)
return false;
const { deleteSelf, deleteAlien } = getFileRoleActions(access);
return deleteSelf || deleteAlien;
};
canCopyItems = (item) => {
const { rootFolderType, access } = item;
if (
rootFolderType === FolderType.TRASH ||
rootFolderType === FolderType.Favorites ||
rootFolderType === FolderType.Recent
// || rootFolderType === FolderType.Privacy
)
return false;
const { canCopy } = getFileRoleActions(access);
return canCopy;
};
canDuplicateFile = (item) => {
const { rootFolderType, access } = item;
if (rootFolderType === FolderType.Archive)
return getArchiveFileRoleActions(access).canDuplicate;
if (
rootFolderType === FolderType.TRASH ||
rootFolderType === FolderType.Favorites ||
rootFolderType === FolderType.Recent
// || rootFolderType === FolderType.Privacy
)
return false;
return getFileRoleActions(access).canDuplicate;
};
canChangeUserType = (user) => {
const { id, isOwner } = this.authStore.userStore.user;
@ -423,19 +156,6 @@ class AccessRightsStore {
return false;
};
canViewUsers = (room) => {
const { rootFolderType } = this.selectedFolderStore;
if (!room) return false;
const options =
rootFolderType === FolderType.Archive
? getArchiveRoomRoleActions(room.access)
: getRoomRoleActions(room.access);
return options.viewUsers;
};
}
export default AccessRightsStore;

View File

@ -163,7 +163,7 @@ class ContextOptionsStore {
this.dialogsStore.setCopyPanelVisible(true);
};
showVersionHistory = (id) => {
showVersionHistory = (id, security) => {
const {
fetchFileVersions,
setIsVerHistoryPanel,
@ -171,7 +171,7 @@ class ContextOptionsStore {
if (this.treeFoldersStore.isRecycleBinFolder) return;
fetchFileVersions(id + "");
fetchFileVersions(id + "", security);
setIsVerHistoryPanel(true);
};
@ -257,7 +257,7 @@ class ContextOptionsStore {
onClickLinkEdit = (item) => {
const { setConvertItem, setConvertDialogVisible } = this.dialogsStore;
const canConvert = this.settingsStore.canConvert(item.fileExst);
const canConvert = item.viewAccessability?.Convert;
if (canConvert) {
setConvertItem(item);
@ -497,8 +497,9 @@ class ContextOptionsStore {
const isRootRoom = item.isRoom && rootFolderId === id;
const isShareable = item.canShare;
const isMedia = this.settingsStore.isMediaOrImage(item.fileExst);
const isCanWebEdit = this.settingsStore.canWebEdit(item.fileExst);
const isMedia =
item.viewAccessability?.ImageView || item.viewAccessability?.MediaView;
const isCanWebEdit = item.viewAccessability?.WebEdit;
const hasInfoPanel = contextOptions.includes("show-info");
const emailSendIsDisabled = true;
@ -535,7 +536,7 @@ class ContextOptionsStore {
key: "show-version-history",
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
onClick: () => this.showVersionHistory(item.id, item.access),
onClick: () => this.showVersionHistory(item.id, item.security),
disabled: false,
},
]
@ -551,7 +552,7 @@ class ContextOptionsStore {
key: "finalize-version",
label: t("FinalizeVersion"),
icon: "images/history-finalized.react.svg",
onClick: () => this.finalizeVersion(item.id, item.access),
onClick: () => this.finalizeVersion(item.id, item.security),
disabled: false,
},
{
@ -560,7 +561,7 @@ class ContextOptionsStore {
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
onClick: () =>
this.showVersionHistory(item.id, item.access),
this.showVersionHistory(item.id, item.security),
disabled: false,
},
],
@ -580,7 +581,7 @@ class ContextOptionsStore {
key: "show-version-history",
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
onClick: () => this.showVersionHistory(item.id, item.access),
onClick: () => this.showVersionHistory(item.id, item.security),
disabled: false,
},
]

View File

@ -16,6 +16,7 @@ import {
ConflictResolveType,
FileAction,
FileStatus,
FolderType,
} from "@docspace/common/constants";
import { makeAutoObservable } from "mobx";
import { isMobile } from "react-device-detect";
@ -1275,18 +1276,11 @@ class FilesActionStore {
selection,
} = this.filesStore;
const {
canCopyItems,
canDeleteItems,
canMoveItems,
canArchiveRoom,
canRemoveRoom,
} = this.accessRightsStore;
const { access, rootFolderType } = this.selectedFolderStore;
const { rootFolderType } = this.selectedFolderStore;
switch (option) {
case "copy":
const canCopy = canCopyItems({ access, rootFolderType });
const canCopy = selection.map((s) => s.security?.Copy).filter((s) => s);
return hasSelection && canCopy;
case "showInfo":
@ -1295,35 +1289,33 @@ class FilesActionStore {
case "downloadAs":
return canConvertSelected;
case "moveTo":
const canMove = canMoveItems({
access,
rootFolderType,
editing: allFilesIsEditing,
});
return hasSelection && canMove;
const canMove = selection.every((s) => s.security?.Move);
return (
hasSelection &&
!allFilesIsEditing &&
canMove &&
rootFolderType !== FolderType.TRASH
);
case "archive":
case "unarchive":
const canArchive = selection
.map((s) => canArchiveRoom(s))
.map((s) => s.security?.Move)
.filter((s) => s);
return canArchive.length > 0;
case "delete-room":
const canRemove = selection
.map((s) => canRemoveRoom(s))
.map((s) => s.security?.Delete)
.filter((r) => r);
return canRemove.length > 0;
case "delete":
const canDelete = canDeleteItems({
access,
rootFolderType,
editing: allFilesIsEditing,
});
const canDelete = selection.every((s) => s.security?.Delete);
return canDelete && hasSelection;
return !allFilesIsEditing && canDelete && hasSelection;
}
};
@ -1778,10 +1770,11 @@ class FilesActionStore {
const { setMediaViewerData } = this.mediaViewerDataStore;
const { setConvertDialogVisible, setConvertItem } = this.dialogsStore;
const isMediaOrImage = this.settingsStore.isMediaOrImage(item.fileExst);
const canConvert = this.settingsStore.canConvert(item.fileExst);
const canWebEdit = this.settingsStore.canWebEdit(item.fileExst);
const canViewedDocs = this.settingsStore.canViewedDocs(item.fileExst);
const isMediaOrImage =
item.viewAccessability?.ImageView || item.viewAccessability?.MediaView;
const canConvert = item.viewAccessability?.Convert;
const canWebEdit = item.viewAccessability?.WebEdit;
const canViewedDocs = item.viewAccessability?.WebView;
const { id, viewUrl, providerKey, fileStatus, encrypted, isFolder } = item;
if (encrypted && isPrivacyFolder) return checkProtocol(item.id, true);

View File

@ -26,7 +26,6 @@ import {
import { isDesktop } from "@docspace/components/utils/device";
import { getContextMenuKeysByType } from "SRC_DIR/helpers/plugins";
import { PluginContextMenuItemType } from "SRC_DIR/helpers/plugins/constants";
import { getArchiveRoomRoleActions } from "@docspace/common/utils/actions";
import debounce from "lodash.debounce";
const { FilesFilter, RoomsFilter } = api;
@ -34,6 +33,11 @@ const storageViewAs = localStorage.getItem("viewAs");
let requestCounter = 0;
const NotFoundHttpCode = 404;
const ForbiddenHttpCode = 403;
const PaymentRequiredHttpCode = 402;
const UnauthorizedHttpCode = 401;
class FilesStore {
authStore;
@ -102,6 +106,8 @@ class FilesStore {
tempActionFilesIds = [];
operationAction = false;
isErrorRoomNotAvailable = false;
constructor(
authStore,
selectedFolderStore,
@ -809,6 +815,7 @@ class FilesStore {
if (folderId === "@my" && this.authStore.userStore.user.isVisitor)
return this.fetchRooms();
this.isErrorRoomNotAvailable = false;
this.isLoadedFetchFiles = false;
const filterStorageItem =
@ -951,18 +958,23 @@ class FilesStore {
})
.catch((err) => {
console.error(err);
toastr.error(err);
if (requestCounter > 0) return;
requestCounter++;
setTimeout(() => {
window.location.href = combineUrl(
AppServerConfig.proxyURL,
config.homepage,
"/rooms/shared/"
);
}, 5000);
const isUserError = [
NotFoundHttpCode,
ForbiddenHttpCode,
PaymentRequiredHttpCode,
UnauthorizedHttpCode,
].includes(err?.response?.status);
if (isUserError) {
this.isErrorRoomNotAvailable = true;
this.isEmptyPage = true;
} else {
toastr.error(err);
}
})
.finally(() => {
this.isLoadedFetchFiles = true;
@ -1066,7 +1078,7 @@ class FilesStore {
this.setCreatedItem(null);
}
this.isErrorRoomNotAvailable = false;
return Promise.resolve(selectedFolder);
})
.catch((err) => {
@ -1136,7 +1148,7 @@ class FilesStore {
return newOptions.filter((o) => o);
};
getFilesContextOptions = (item, canOpenPlayer) => {
getFilesContextOptions = (item) => {
const isFile = !!item.fileExst || item.contentLength;
const isRoom = !!item.roomType;
const isFavorite =
@ -1155,8 +1167,6 @@ class FilesStore {
const { isRecycleBinFolder, isMy, isArchiveFolder } = this.treeFoldersStore;
const { canFormFillingDocs } = this.filesSettingsStore;
const { enablePlugins } = this.authStore.settingsStore;
const isThirdPartyFolder =
@ -1169,39 +1179,33 @@ class FilesStore {
const pluginAllKeys =
enablePlugins && getContextMenuKeysByType(PluginContextMenuItemType.All);
const canRenameItem = this.accessRightsStore.canRenameItem({
...item,
...isFile,
});
const canRenameItem = item.security?.Rename;
const canMove = this.accessRightsStore.canMoveItems({
...item,
...{ editing: isEditing },
});
const canDelete = this.accessRightsStore.canDeleteItems({
...item,
...{ editing: isEditing },
});
const canDelete = !isEditing && item.security?.Delete;
const canCopy = this.accessRightsStore.canCopyItems(item);
const canCreateCopy = this.accessRightsStore.canDuplicateFile(item);
const canCopy = item.security?.Copy;
const canDuplicate = item.security?.Duplicate;
if (isFile) {
const shouldFillForm = canFormFillingDocs(item.fileExst);
const canLockFile = this.accessRightsStore.canLockFile(item);
const canChangeVersionFileHistory = this.accessRightsStore.canChangeVersionFileHistory(
{ ...item, ...{ editing: isEditing } }
);
const shouldFillForm = item.viewAccessability.WebRestrictedEditing;
const canLockFile = item.security?.Lock;
const canChangeVersionFileHistory =
!isEditing && item.security?.EditHistory;
const canViewVersionFileHistory = this.accessRightsStore.canViewVersionFileHistory(
item
);
const canFillForm = this.accessRightsStore.canFillForm(item);
const canViewVersionFileHistory = item.security?.ReadHistory;
const canFillForm = item.security?.FillForms;
const canEditFile = item.security.Edit && item.viewAccessability.WebEdit;
const canOpenPlayer =
item.viewAccessability.ImageView || item.viewAccessability.MediaView;
const canViewFile = item.viewAccessability.WebView;
const canEditFile = this.accessRightsStore.canEditFile(item);
const isMasterForm = item.fileExst === ".docxf";
const canMakeForm = this.accessRightsStore.canMakeForm(item);
let fileOptions = [
//"open",
@ -1288,14 +1292,14 @@ class FilesStore {
fileOptions = this.removeOptions(fileOptions, ["copy-to"]);
}
if (!canCreateCopy) {
if (!canDuplicate) {
fileOptions = this.removeOptions(fileOptions, ["copy"]);
}
if (!canMove && !canCopy && !canCreateCopy) {
if (!canMove && !canCopy && !canDuplicate) {
fileOptions = this.removeOptions(fileOptions, ["move"]);
}
if (!(isMasterForm && canMakeForm))
if (!(isMasterForm && canDuplicate))
fileOptions = this.removeOptions(fileOptions, ["make-form"]);
if (item.rootFolderType === FolderType.Archive) {
@ -1314,10 +1318,12 @@ class FilesStore {
fileOptions = this.removeOptions(fileOptions, ["convert"]);
}
if (!canViewFile || isRecycleBinFolder) {
fileOptions = this.removeOptions(fileOptions, ["preview"]);
}
if (!canOpenPlayer) {
fileOptions = this.removeOptions(fileOptions, ["view"]);
} else {
fileOptions = this.removeOptions(fileOptions, ["preview"]);
}
if (!isDocuSign) {
@ -1363,36 +1369,20 @@ class FilesStore {
// ]);
// }
if (isRecycleBinFolder) {
if (!isRecycleBinFolder)
fileOptions = this.removeOptions(fileOptions, [
"open",
"open-location",
"view",
"preview",
"restore",
"link-for-room-members",
//"link-for-portal-users",
//"sharing-settings",
//"external-link",
"send-by-email",
"mark-read",
// "mark-as-favorite",
// "remove-from-favorites",
"separator0",
"separator1",
]);
} else {
fileOptions = this.removeOptions(fileOptions, ["restore"]);
if (enablePlugins) {
const pluginFilesKeys = getContextMenuKeysByType(
PluginContextMenuItemType.Files
);
if (enablePlugins && !isRecycleBinFolder) {
const pluginFilesKeys = getContextMenuKeysByType(
PluginContextMenuItemType.Files
);
pluginAllKeys &&
pluginAllKeys.forEach((key) => fileOptions.push(key));
pluginFilesKeys &&
pluginFilesKeys.forEach((key) => fileOptions.push(key));
}
pluginAllKeys && pluginAllKeys.forEach((key) => fileOptions.push(key));
pluginFilesKeys &&
pluginFilesKeys.forEach((key) => fileOptions.push(key));
}
if (!this.canShareOwnerChange(item)) {
@ -1440,17 +1430,15 @@ class FilesStore {
return fileOptions;
} else if (isRoom) {
const canInviteUserInRoom = this.accessRightsStore.canInviteUserInRoom(
item
);
const canRemoveRoom = this.accessRightsStore.canRemoveRoom(item);
const canInviteUserInRoom = item.security?.EditAccess;
const canRemoveRoom = item.security?.Delete;
const canArchiveRoom = this.accessRightsStore.canArchiveRoom(item);
const canPinRoom = this.accessRightsStore.canPinRoom(item);
const canArchiveRoom = item.security?.Move;
const canPinRoom = item.security?.Pin;
const canEditRoom = this.accessRightsStore.canEditRoom(item);
const canEditRoom = item.security?.EditRoom;
const canViewRoomInfo = this.accessRightsStore.canViewRoomInfo(item);
const canViewRoomInfo = item.security?.Read;
let roomOptions = [
"select",
@ -1576,11 +1564,11 @@ class FilesStore {
folderOptions = this.removeOptions(folderOptions, ["copy-to"]);
}
if (!canCreateCopy) {
if (!canDuplicate) {
folderOptions = this.removeOptions(folderOptions, ["copy"]);
}
if (!canMove && !canCopy && !canCreateCopy) {
if (!canMove && !canCopy && !canDuplicate) {
folderOptions = this.removeOptions(folderOptions, ["move"]);
}
@ -2128,6 +2116,8 @@ class FilesStore {
isArchive,
tags,
pinned,
security,
viewAccessability,
} = item;
const thirdPartyIcon = this.thirdPartyStore.getThirdPartyIcon(
@ -2140,9 +2130,8 @@ class FilesStore {
Object.keys(RoomsProviderType).find((key) => key === item.providerKey)
];
const { canConvert, isMediaOrImage } = this.filesSettingsStore;
const canOpenPlayer = isMediaOrImage(item.fileExst);
const canOpenPlayer =
item.viewAccessability?.ImageView || item.viewAccessability?.MediaView;
const previewUrl = canOpenPlayer
? this.getItemUrl(id, false, needConvert, canOpenPlayer)
@ -2161,7 +2150,7 @@ class FilesStore {
const folderUrl = isFolder && this.getItemUrl(id, isFolder, false, false);
const needConvert = canConvert(fileExst);
const needConvert = item.viewAccessability?.Convert;
const isEditing =
(item.fileStatus & FileStatus.IsEditing) === FileStatus.IsEditing;
@ -2244,6 +2233,8 @@ class FilesStore {
pinned,
thirdPartyIcon,
providerType,
security,
viewAccessability,
};
});
@ -2252,8 +2243,6 @@ class FilesStore {
get cbMenuItems() {
const {
isImage,
isVideo,
isDocument,
isPresentation,
isSpreadsheet,
@ -2293,8 +2282,10 @@ class FilesStore {
cbMenu.push(FilterType.PresentationsOnly);
else if (isSpreadsheet(item.fileExst))
cbMenu.push(FilterType.SpreadsheetsOnly);
else if (isImage(item.fileExst)) cbMenu.push(FilterType.ImagesOnly);
else if (isVideo(item.fileExst)) cbMenu.push(FilterType.MediaOnly);
else if (item.viewAccessability?.ImageView)
cbMenu.push(FilterType.ImagesOnly);
else if (item.viewAccessability?.MediaView)
cbMenu.push(FilterType.MediaOnly);
else if (isArchive(item.fileExst)) cbMenu.push(FilterType.ArchiveOnly);
}
@ -2489,20 +2480,19 @@ class FilesStore {
}
get isViewedSelected() {
const { canViewedDocs } = this.filesSettingsStore;
return this.selection.some((selected) => {
if (selected.isFolder === true || !selected.fileExst) return false;
return canViewedDocs(selected.fileExst);
return selected.viewAccessability?.WebView;
});
}
get isMediaSelected() {
const { isMediaOrImage } = this.filesSettingsStore;
return this.selection.some((selected) => {
if (selected.isFolder === true || !selected.fileExst) return false;
return isMediaOrImage(selected.fileExst);
return (
selected.viewAccessability?.ImageView ||
selected.viewAccessability?.MediaView
);
});
}
@ -2543,15 +2533,6 @@ class FilesStore {
}
getOptions = (selection, externalAccess = false) => {
const {
canWebEdit,
canWebComment,
canWebReview,
canFormFillingDocs,
canWebFilterEditing,
canConvert,
} = this.filesSettingsStore;
if (selection[0].encrypted) {
return ["FullAccess", "DenyAccess"];
}
@ -2560,19 +2541,21 @@ class FilesStore {
AccessOptions.push("ReadOnly", "DenyAccess");
const webEdit = selection.find((x) => canWebEdit(x.fileExst));
const webEdit = selection.find((x) => x.viewAccessability?.WebEdit);
const webComment = selection.find((x) => canWebComment(x.fileExst));
const webComment = selection.find((x) => x.viewAccessability?.WebComment);
const webReview = selection.find((x) => canWebReview(x.fileExst));
const webReview = selection.find((x) => x.viewAccessability?.WebReview);
const formFillingDocs = selection.find((x) =>
canFormFillingDocs(x.fileExst)
const formFillingDocs = selection.find(
(x) => x.viewAccessability?.WebRestrictedEditing
);
const webFilter = selection.find((x) => canWebFilterEditing(x.fileExst));
const webFilter = selection.find(
(x) => x.viewAccessability?.WebCustomFilterEditing
);
const webNeedConvert = selection.find((x) => canConvert(x.fileExst));
const webNeedConvert = selection.find((x) => x.viewAccessability?.Convert);
if ((webEdit && !webNeedConvert) || !externalAccess)
AccessOptions.push("FullAccess");
@ -2897,15 +2880,11 @@ class FilesStore {
}
get roomsForRestore() {
return this.folders.filter(
(f) => getArchiveRoomRoleActions(f.access).restore
);
return this.folders.filter((f) => f.security.Move);
}
get roomsForDelete() {
return this.folders.filter(
(f) => getArchiveRoomRoleActions(f.access).delete
);
return this.folders.filter((f) => f.security.Delete);
}
}

View File

@ -46,7 +46,6 @@ class MediaViewerDataStore {
};
get playlist() {
const { isMediaOrImage } = this.settingsStore;
const { files } = this.filesStore;
const filesList = [...files];
@ -66,7 +65,8 @@ class MediaViewerDataStore {
if (filesList.length > 0) {
filesList.forEach((file) => {
const canOpenPlayer = isMediaOrImage(file.fileExst);
const canOpenPlayer =
file.viewAccessability.ImageView || file.viewAccessability.MediaView;
if (canOpenPlayer) {
playlist.push({
id: id,

View File

@ -29,6 +29,7 @@ class SelectedFolderStore {
tags = null;
rootFolderId = null;
settingsStore = null;
security = null;
constructor(settingsStore) {
makeAutoObservable(this);
@ -66,6 +67,7 @@ class SelectedFolderStore {
this.logo = null;
this.tags = null;
this.rootFolderId = null;
this.security = null;
};
setParentId = (parentId) => {

View File

@ -194,41 +194,12 @@ class SettingsStore {
this.hideConfirmConvertSave = hideConfirmConvertSave;
};
canWebEdit = (extension) => presentInArray(this.extsWebEdited, extension);
canViewedDocs = (extension) =>
presentInArray(this.extsWebPreviewed, extension);
canConvert = (extension) => presentInArray(this.extsMustConvert, extension);
canWebComment = (extension) =>
presentInArray(this.extsWebCommented, extension);
canWebReview = (extension) => presentInArray(this.extsWebReviewed, extension);
canFormFillingDocs = (extension) =>
presentInArray(this.extsWebRestrictedEditing, extension);
canWebFilterEditing = (extension) =>
presentInArray(this.extsWebCustomFilterEditing, extension);
isMediaOrImage = (fileExst) => {
if (
this.extsVideo.includes(fileExst) ||
this.extsImage.includes(fileExst) ||
this.extsAudio.includes(fileExst)
) {
return true;
}
return false;
};
isArchive = (extension) => presentInArray(this.extsArchive, extension);
isImage = (extension) => presentInArray(this.extsImage, extension);
isVideo = (extension) => presentInArray(this.extsVideo, extension);
isSound = (extension) => presentInArray(this.extsAudio, extension);
isHtml = (extension) => presentInArray(this.html, extension);

View File

@ -901,7 +901,7 @@ class UploadDataStore {
path
);
})
.catch((err) => {
.catch((error) => {
if (this.files[indexOfFile] === undefined) {
this.primaryProgressDataStore.setPrimaryProgressBarData({
icon: "upload",
@ -911,8 +911,18 @@ class UploadDataStore {
});
return Promise.resolve();
}
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
this.files[indexOfFile].error = err;
this.files[indexOfFile].error = errorMessage;
//dispatch(setUploadData(uploadData));

View File

@ -6,7 +6,8 @@ import { FileStatus } from "@docspace/common/constants";
class VersionHistoryStore {
isVisible = false;
fileId = null;
fileAccess = null;
fileSecurity = null;
versions = null;
filesStore = null;
showProgressBar = false;
@ -62,8 +63,9 @@ class VersionHistoryStore {
setVerHistoryFileId = (fileId) => {
this.fileId = fileId;
};
setVerHistoryFileAccess = (access) => {
this.fileAccess = access;
setVerHistoryFileSecurity = (security) => {
this.fileSecurity = security;
};
setVersions = (versions) => {
this.versions = versions;
@ -96,7 +98,7 @@ class VersionHistoryStore {
fetchFileVersions = (fileId, access) => {
if (this.fileId !== fileId || !this.versions) {
this.setVerHistoryFileId(fileId);
this.setVerHistoryFileAccess(access);
this.setVerHistoryFileSecurity(access);
return api.files
.getFileVersionInfo(fileId)

View File

@ -1,143 +1,9 @@
import { ShareAccessRights } from "../../constants/index";
import {
RoomsActions,
OwnerRoomsActions,
RoomAdminRoomsActions,
EditorRoomsActions,
FormFillerRoomsActions,
ReviewerRoomsActions,
CommentatorRoomsActions,
ViewerRoomsActions,
} from "./Rooms";
import {
ArchiveRoomsActions,
OwnerArchiveRoomsActions,
RoomAdminArchiveRoomsActions,
EditorArchiveRoomsActions,
FormFillerArchiveRoomsActions,
ReviewerArchiveRoomsActions,
CommentatorArchiveRoomsActions,
ViewerArchiveRoomsActions,
} from "./ArchiveRoom";
import {
FilesActions,
OwnerFilesActions,
RoomAdminFilesActions,
EditorFilesActions,
FormFillerFilesActions,
ReviewerFilesActions,
CommentatorFilesActions,
ViewerFilesActions,
} from "./Files";
import {
ArchiveFilesActions,
OwnerArchiveFilesActions,
RoomAdminArchiveFilesActions,
EditorArchiveFilesActions,
FormFillerArchiveFilesActions,
ReviewerArchiveFilesActions,
CommentatorArchiveFilesActions,
ViewerArchiveFilesActions,
} from "./ArchiveFiles";
import {
OwnerAccountsActions,
DocSpaceAdminAccountsActions,
RoomAdminAccountsActions,
} from "./Accounts";
export const getRoomRoleActions = (access) => {
switch (access) {
case ShareAccessRights.None:
case ShareAccessRights.FullAccess:
return OwnerRoomsActions;
case ShareAccessRights.RoomManager:
return RoomAdminRoomsActions;
case ShareAccessRights.Editing:
return EditorRoomsActions;
case ShareAccessRights.FormFilling:
return FormFillerRoomsActions;
case ShareAccessRights.Review:
return ReviewerRoomsActions;
case ShareAccessRights.Comment:
return CommentatorRoomsActions;
case ShareAccessRights.ReadOnly:
return ViewerRoomsActions;
default:
return RoomsActions;
}
};
export const getFileRoleActions = (access) => {
switch (access) {
case ShareAccessRights.None:
case ShareAccessRights.FullAccess:
return OwnerFilesActions;
case ShareAccessRights.RoomManager:
return RoomAdminFilesActions;
case ShareAccessRights.Editing:
return EditorFilesActions;
case ShareAccessRights.FormFilling:
return FormFillerFilesActions;
case ShareAccessRights.Review:
return ReviewerFilesActions;
case ShareAccessRights.Comment:
return CommentatorFilesActions;
case ShareAccessRights.ReadOnly:
return ViewerFilesActions;
default:
return FilesActions;
}
};
export const getArchiveRoomRoleActions = (access) => {
switch (access) {
case ShareAccessRights.None:
case ShareAccessRights.FullAccess:
return OwnerArchiveRoomsActions;
case ShareAccessRights.RoomManager:
return RoomAdminArchiveRoomsActions;
case ShareAccessRights.Editing:
return EditorArchiveRoomsActions;
case ShareAccessRights.FormFilling:
return FormFillerArchiveRoomsActions;
case ShareAccessRights.Review:
return ReviewerArchiveRoomsActions;
case ShareAccessRights.Comment:
return CommentatorArchiveRoomsActions;
case ShareAccessRights.ReadOnly:
return ViewerArchiveRoomsActions;
default:
return ArchiveRoomsActions;
}
};
export const getArchiveFileRoleActions = (access) => {
switch (access) {
case ShareAccessRights.None:
case ShareAccessRights.FullAccess:
return OwnerArchiveFilesActions;
case ShareAccessRights.RoomManager:
return RoomAdminArchiveFilesActions;
case ShareAccessRights.Editing:
return EditorArchiveFilesActions;
case ShareAccessRights.FormFilling:
return FormFillerArchiveFilesActions;
case ShareAccessRights.Review:
return ReviewerArchiveFilesActions;
case ShareAccessRights.Comment:
return CommentatorArchiveFilesActions;
case ShareAccessRights.ReadOnly:
return ViewerArchiveFilesActions;
default:
return ArchiveFilesActions;
}
};
export const getAccountsTypeActions = (isAdmin, isOwner) => {
if (isOwner) return OwnerAccountsActions;

View File

@ -16,7 +16,7 @@ class AxiosClient {
if (apiOrigin !== "") {
headers = {
'Access-Control-Allow-Credentials' : true
"Access-Control-Allow-Credentials": true,
};
}
@ -41,7 +41,7 @@ class AxiosClient {
baseURL: apiBaseURL,
responseType: "json",
timeout: apiTimeout, // default is `0` (no timeout)
withCredentials: true
withCredentials: true,
};
if (headers) {
@ -106,11 +106,11 @@ class AxiosClient {
};
const onError = (error) => {
//console.error("Request Failed:", error);
console.log("Request Failed:", { error });
let errorText = error.response
? this.getResponseError(error.response)
: error.message;
// let errorText = error.response
// ? this.getResponseError(error.response)
// : error.message;
if (error?.response?.status === 401 && this.isSSR) errorText = 401;
@ -138,7 +138,7 @@ class AxiosClient {
break;
}
return Promise.reject(errorText || error);
return Promise.reject(error);
} else {
switch (error.response?.status) {
case 401:

View File

@ -61,19 +61,19 @@ const RowContent = (props) => {
onClick={onClick}
style={style}
widthProp={sectionWidth}
isMobile={isMobile}
isMobile={true}
>
<MainContainerWrapper
disableSideInfo={disableSideInfo}
mainContainerWidth={mainContainerWidth}
widthProp={sectionWidth}
isMobile={isMobile}
isMobile={true}
className="row-main-container-wrapper"
>
<MainContainer
className="rowMainContainer"
widthProp={sectionWidth}
isMobile={isMobile}
isMobile={true}
>
{children[0]}
</MainContainer>
@ -90,7 +90,7 @@ const RowContent = (props) => {
element.props && element.props.containerMinWidth
}
widthProp={sectionWidth}
isMobile={isMobile}
isMobile={true}
>
{element}
</SideContainerWrapper>
@ -102,7 +102,7 @@ const RowContent = (props) => {
className="row-content_tablet-side-info"
color={sideColor}
widthProp={sectionWidth}
isMobile={isMobile}
isMobile={true}
convertSideInfo={convertSideInfo}
>
{sideInfo}
@ -127,7 +127,6 @@ RowContent.propTypes = {
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
sectionWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
isMobile: PropTypes.bool,
convertSideInfo: PropTypes.bool,
};

View File

@ -49,7 +49,7 @@ const StyledRowContent = styled.div`
${(props) =>
(!props.disableSideInfo &&
props.widthProp &&
props.widthProp < size.tablet) ||
props.widthProp <= size.tablet) ||
props.isMobile
? `${containerTabletStyle}`
: `
@ -75,7 +75,7 @@ const MainContainerWrapper = styled.div`
${(props) =>
(!props.disableSideInfo &&
props.widthProp &&
props.widthProp < size.tablet) ||
props.widthProp <= size.tablet) ||
props.isMobile
? css`
${mainWrapperTabletStyle}
@ -95,7 +95,7 @@ const MainContainer = styled.div`
max-width: 100%;
${(props) =>
(props.widthProp && props.widthProp < size.tablet) || props.isMobile
(props.widthProp && props.widthProp <= size.tablet) || props.isMobile
? `${mainContainerTabletStyle}`
: `
@media ${tablet} {
@ -116,7 +116,7 @@ const SideContainerWrapper = styled.div`
${commonCss};
${(props) =>
(props.widthProp && props.widthProp < size.tablet) || props.isMobile
(props.widthProp && props.widthProp <= size.tablet) || props.isMobile
? `${truncateCss}`
: `
@media ${tablet} {
@ -139,7 +139,7 @@ const SideContainerWrapper = styled.div`
${(props) =>
(!props.disableSideInfo &&
props.widthProp &&
props.widthProp < size.tablet) ||
props.widthProp <= size.tablet) ||
props.isMobile
? `display: none;`
: `
@ -154,7 +154,7 @@ const TabletSideInfo = styled.div`
display: none;
${(props) => (props.color ? `color: ${props.color};` : null)}
${(props) =>
(props.widthProp && props.widthProp < size.tablet) || props.isMobile
(props.widthProp && props.widthProp <= size.tablet) || props.isMobile
? `${sideInfoTabletStyle}`
: `
@media ${tablet} {

View File

@ -140,15 +140,28 @@ function fatal(data, title, timeout, withCross, centerPosition) {
}
function error(data, title, timeout, withCross, centerPosition) {
console.log("toast error: ", { data });
const dataType = typeof data;
const message =
dataType === "string"
? data
: dataType === "object" && data.statusText
? data.statusText
: dataType === "object" && data.message
? data.message
: "";
let message = "";
if (dataType === "string") {
message = data;
} else if (dataType === "object") {
message =
data?.response?.data?.error?.message ||
data?.statusText ||
data?.message ||
"";
}
// const message =
// dataType === "string"
// ? data
// : dataType === "object" && data.statusText
// ? data.statusText
// : dataType === "object" && data.message
// ? data.message
// : "";
return notify(
"error",

View File

@ -22,14 +22,9 @@ import {
import { EditorWrapper } from "../components/StyledEditor";
import { useTranslation } from "react-i18next";
import withDialogs from "../helpers/withDialogs";
import { canConvert } from "../helpers/utils";
import { assign } from "@docspace/common/utils";
import toastr from "@docspace/components/toast/toastr";
import { DocumentEditor } from "@onlyoffice/document-editor-react";
import {
getArchiveFileRoleActions,
getFileRoleActions,
} from "@docspace/common/utils/actions";
toast.configure();
@ -103,9 +98,7 @@ function Editor({
const { t } = useTranslation(["Editor", "Common"]);
if (fileInfo) {
userAccessRights = isArchiveFolderRoot
? getArchiveFileRoleActions(fileInfo.access)
: getFileRoleActions(fileInfo.access);
userAccessRights = fileInfo.security;
}
useEffect(() => {
if (error && mfReady) {
@ -131,9 +124,9 @@ function Editor({
if (
!view &&
fileInfo &&
fileInfo.canWebRestrictedEditing &&
fileInfo.canFillForms &&
!fileInfo.canEdit
fileInfo.viewAccessability.WebRestrictedEditing &&
fileInfo.security.FillForms &&
!fileInfo.security.Edit
) {
try {
initForm();
@ -166,7 +159,7 @@ function Editor({
url.indexOf("#message/") > -1 &&
fileInfo &&
fileInfo?.fileExst &&
canConvert(fileInfo.fileExst, filesSettings)
fileInfo?.viewAccessability?.Convert
) {
showDocEditorMessage(url);
}
@ -228,7 +221,7 @@ function Editor({
if (index) {
let convertUrl = url.substring(0, index);
if (canConvert(fileInfo.fileExst, filesSettings)) {
if (fileInfo?.viewAccessability?.Convert) {
const newUrl = await convertDocumentUrl();
if (newUrl) {
convertUrl = newUrl.webUrl;
@ -279,9 +272,20 @@ function Editor({
),
history: getDocumentHistory(updateVersions, historyLength),
});
} catch (e) {
} catch (error) {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
docEditor.refreshHistory({
error: `${e}`, //TODO: maybe need to display something else.
error: `${errorMessage}`, //TODO: maybe need to display something else.
});
}
};
@ -330,9 +334,19 @@ function Editor({
currentVersion: getCurrentDocumentVersion(fileHistory, historyLength),
history: getDocumentHistory(fileHistory, historyLength),
});
} catch (e) {
} catch (error) {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
docEditor.refreshHistory({
error: `${e}`, //TODO: maybe need to display something else.
error: `${errorMessage}`, //TODO: maybe need to display something else.
});
}
};
@ -361,9 +375,20 @@ function Editor({
url: versionDifference.url,
version,
});
} catch (e) {
} catch (error) {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
docEditor.setHistoryData({
error: `${e}`, //TODO: maybe need to display something else.
error: `${errorMessage}`, //TODO: maybe need to display something else.
version,
});
}
@ -551,11 +576,11 @@ function Editor({
// onRequestSharingSettings = onSDKRequestSharingSettings;
// }
if (userAccessRights.rename) {
if (userAccessRights.Rename) {
onRequestRename = onSDKRequestRename;
}
if (userAccessRights.viewVersionHistory) {
if (userAccessRights.ReadHistory) {
onRequestHistory = onSDKRequestHistory;
}
@ -569,7 +594,7 @@ function Editor({
onRequestCompareFile = onSDKRequestCompareFile;
}
if (userAccessRights.changeVersionHistory) {
if (userAccessRights.EditHistory) {
onRequestRestore = onSDKRequestRestore;
}

View File

@ -1,11 +1,5 @@
import pkg from "../../../package.json";
export const canConvert = (extension, filesSettings) => {
const array = filesSettings?.extsMustConvert || [];
const result = array.findIndex((item) => item === extension);
return result === -1 ? false : true;
};
export const initI18n = (initialI18nStoreASC) => {
if (!initialI18nStoreASC || window.i18n) return;

View File

@ -164,9 +164,20 @@ const LoginForm: React.FC<ILoginFormProps> = ({
else window.location.replace("/"); //TODO: save { user, hash } for tfa
})
.catch((error) => {
let errorMessage = "";
if (typeof error === "object") {
errorMessage =
error?.response?.data?.error?.message ||
error?.statusText ||
error?.message ||
"";
} else {
errorMessage = error;
}
setIsEmailErrorShow(true);
setErrorText(error);
setPasswordValid(!error);
setErrorText(errorMessage);
setPasswordValid(!errorMessage);
setIsLoading(false);
focusInput();
});

View File

@ -44,10 +44,10 @@ public class FileDto<T> : FileEntryDto<T>
public Thumbnail ThumbnailStatus { get; set; }
public bool? Locked { get; set; }
public string LockedBy { get; set; }
public bool CanWebRestrictedEditing { get; set; }
public bool CanFillForms { get; set; }
public bool DenyDownload { get; set; }
public bool DenySharing { get; set; }
public IDictionary<Accessability, bool> ViewAccessability { get; set; }
protected internal override FileEntryType EntryType { get => FileEntryType.File; }
public FileDto() { }
@ -138,6 +138,8 @@ public class FileDtoHelper : FileEntryDtoHelper
}
}
result.ViewAccessability = _fileUtility.GetAccessability(file.Title);
return result;
}
@ -156,8 +158,6 @@ public class FileDtoHelper : FileEntryDtoHelper
result.Encrypted = file.Encrypted.NullIfDefault();
result.Locked = file.Locked.NullIfDefault();
result.LockedBy = file.LockedBy;
result.CanWebRestrictedEditing = _fileUtility.CanWebRestrictedEditing(file.Title);
result.CanFillForms = await _fileSecurity.CanFillFormsAsync(file);
result.DenyDownload = file.DenyDownload;
result.DenySharing = file.DenySharing;
result.Access = file.Access;

View File

@ -24,6 +24,8 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using static ASC.Files.Core.Security.FileSecurity;
namespace ASC.Files.Core.ApiModels.ResponseDto;
public abstract class FileEntryDto
@ -67,7 +69,7 @@ public abstract class FileEntryDto<T> : FileEntryDto
public T Id { get; set; }
public T RootFolderId { get; set; }
public bool CanShare { get; set; }
public bool CanEdit { get; set; }
public IDictionary<FilesSecurityActions, bool> Security { get; set; }
protected FileEntryDto(FileEntry<T> entry)
: base(entry)
@ -82,6 +84,8 @@ public abstract class FileEntryDto<T> : FileEntryDto
[Scope]
public class FileEntryDtoHelper
{
private readonly ApiDateTimeHelper _apiDateTimeHelper;
private readonly EmployeeDtoHelper _employeeWraperHelper;
private readonly FileSharingHelper _fileSharingHelper;
@ -90,7 +94,8 @@ public class FileEntryDtoHelper
public FileEntryDtoHelper(
ApiDateTimeHelper apiDateTimeHelper,
EmployeeDtoHelper employeeWraperHelper,
FileSharingHelper fileSharingHelper, FileSecurity fileSecurity
FileSharingHelper fileSharingHelper,
FileSecurity fileSecurity
)
{
_apiDateTimeHelper = apiDateTimeHelper;
@ -101,6 +106,11 @@ public class FileEntryDtoHelper
protected internal async Task<T> GetAsync<T, TId>(FileEntry<TId> entry) where T : FileEntryDto<TId>, new()
{
if (entry.Security == null)
{
entry = await _fileSecurity.SetSecurity(new[] { entry }.ToAsyncEnumerable()).FirstAsync();
}
return new T
{
Id = entry.Id,
@ -117,7 +127,7 @@ public class FileEntryDtoHelper
ProviderKey = entry.ProviderKey,
ProviderId = entry.ProviderId.NullIfDefault(),
CanShare = await _fileSharingHelper.CanSetAccessAsync(entry),
CanEdit = await _fileSecurity.CanEditAsync(entry)
Security = entry.Security
};
}
}

View File

@ -34,6 +34,7 @@ public class FileShareDto
public object SharedTo { get; set; }
public bool IsLocked { get; set; }
public bool IsOwner { get; set; }
public bool CanEditAccess { get; set; }
public static FileShareDto GetSample()
{
@ -77,7 +78,8 @@ public class FileShareDtoHelper
var result = new FileShareDto
{
IsOwner = aceWrapper.Owner,
IsLocked = aceWrapper.LockedRights
IsLocked = aceWrapper.LockedRights,
CanEditAccess = aceWrapper.CanEditAccess,
};
if (aceWrapper.SubjectGroup)

View File

@ -24,6 +24,8 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using static ASC.Files.Core.Security.FileSecurity;
namespace ASC.Files.Core;
[Serializable]
@ -110,11 +112,15 @@ public abstract class FileEntry<T> : FileEntry, ICloneable, IFileEntry<T>
{
public T Id { get; set; }
public T ParentId { get; set; }
public IDictionary<FilesSecurityActions, bool> Security { get; set; }
private T _folderIdDisplay;
private readonly GlobalFolderHelper _globalFolderHelper;
private readonly FilesSettingsHelper _filesSettingsHelper;
private readonly FileDateTime _fileDateTime;
protected FileEntry() { }
protected FileEntry(

View File

@ -349,14 +349,16 @@ public class FileStorageService<T> //: IFileStorageService
var prevVisible = breadCrumbs.ElementAtOrDefault(breadCrumbs.Count - 2);
if (prevVisible != null && !DocSpaceHelper.IsRoom(parent.FolderType))
{
if (prevVisible is Folder<string> f1)
if (prevVisible.FileEntryType == FileEntryType.Folder)
{
parent.ParentId = (T)Convert.ChangeType(f1.Id, typeof(T));
}
if (prevVisible is Folder<int> f2)
{
parent.ParentId = (T)Convert.ChangeType(f2.Id, typeof(T));
if (prevVisible is Folder<string> f1)
{
parent.ParentId = (T)Convert.ChangeType(f1.Id, typeof(T));
}
else if (prevVisible is Folder<int> f2)
{
parent.ParentId = (T)Convert.ChangeType(f2.Id, typeof(T));
}
}
}
@ -364,9 +366,20 @@ public class FileStorageService<T> //: IFileStorageService
|| parent.FolderType == FolderType.SHARE
|| parent.RootFolderType == FolderType.Privacy;
entries = entries.Where(x => x.FileEntryType == FileEntryType.Folder ||
x is File<string> f1 && !_fileConverter.IsConverting(f1) ||
x is File<int> f2 && !_fileConverter.IsConverting(f2));
entries = entries.Where(x =>
{
if (x.FileEntryType == FileEntryType.Folder)
{
return true;
}
if (x is File<string> f1)
{
return !_fileConverter.IsConverting(f1);
}
return x is File<int> f2 && !_fileConverter.IsConverting(f2);
});
var result = new DataWrapper<T>
{
@ -374,14 +387,17 @@ public class FileStorageService<T> //: IFileStorageService
Entries = entries.ToList(),
FolderPathParts = new List<object>(breadCrumbs.Select(f =>
{
if (f is Folder<string> f1)
if (f.FileEntryType == FileEntryType.Folder)
{
return (object)f1.Id;
}
if (f is Folder<string> f1)
{
return (object)f1.Id;
}
if (f is Folder<int> f2)
{
return f2.Id;
if (f is Folder<int> f2)
{
return f2.Id;
}
}
return 0;
@ -393,17 +409,6 @@ public class FileStorageService<T> //: IFileStorageService
return result;
}
public async Task<object> GetFolderItemsXmlAsync(T parentId, int from, int count, FilterType filter, bool subjectGroup, string subjectID, string search, bool searchInContent, bool withSubfolders, OrderBy orderBy)
{
var folderItems = await GetFolderItemsAsync(parentId, from, count, filter, subjectGroup, subjectID, search, searchInContent, withSubfolders, orderBy);
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(_serializer.ToXml(folderItems))
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
return response;
}
public async Task<List<FileEntry>> GetItemsAsync<TId>(IEnumerable<TId> filesId, IEnumerable<TId> foldersId, FilterType filter, bool subjectGroup, string subjectID, string search)
{
var subjectId = string.IsNullOrEmpty(subjectID) ? Guid.Empty : new Guid(subjectID);
@ -1312,7 +1317,7 @@ public class FileStorageService<T> //: IFileStorageService
}
ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound);
ErrorIf(!readLink && !await _fileSecurity.CanReadAsync(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
ErrorIf(!readLink && !await _fileSecurity.CanReadHistoryAsync(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
ErrorIf(file.ProviderEntry, FilesCommonResource.ErrorMassage_BadRequest);
await foreach (var f in fileDao.GetEditHistoryAsync(_documentServiceHelper, file.Id))
@ -1340,7 +1345,7 @@ public class FileStorageService<T> //: IFileStorageService
}
ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound);
ErrorIf(!readLink && !await _fileSecurity.CanReadAsync(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
ErrorIf(!readLink && !await _fileSecurity.CanReadHistoryAsync(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
ErrorIf(file.ProviderEntry, FilesCommonResource.ErrorMassage_BadRequest);
var result = new EditHistoryDataDto
@ -2696,7 +2701,7 @@ public class FileStorageService<T> //: IFileStorageService
var room = await folderDao.GetFolderAsync(folderId);
ErrorIf(room == null, FilesCommonResource.ErrorMassage_FolderNotFound);
ErrorIf(!await _fileSecurity.CanReadAsync(room), FilesCommonResource.ErrorMassage_SecurityException_ReadFolder);
ErrorIf(!await _fileSecurity.CanPinAsync(room), FilesCommonResource.ErrorrMessage_PinRoom);
var tagDao = GetTagDao();
var tag = Tag.Pin(_authContext.CurrentAccount.ID, room);

File diff suppressed because it is too large Load Diff

View File

@ -119,12 +119,12 @@ public class RoomLogoManager
return room;
}
public async Task<Folder<T>> DeleteAsync<T>(T id)
public async Task<Folder<T>> DeleteAsync<T>(T id, bool checkPermissions = true)
{
var folderDao = _daoFactory.GetFolderDao<T>();
var room = await folderDao.GetFolderAsync(id);
if (!await _fileSecurity.CanEditRoomAsync(room))
if (checkPermissions && !await _fileSecurity.CanEditRoomAsync(room))
{
throw new InvalidOperationException("You don't have permission to edit the room");
}

View File

@ -174,6 +174,20 @@ public class FileUtilityConfiguration
}
}
public enum Accessability
{
ImageView,
MediaView,
WebView,
WebEdit,
WebReview,
WebCustomFilterEditing,
WebRestrictedEditing,
WebComment,
CoAuhtoring,
Convert
}
[Scope]
public class FileUtility
{
@ -296,6 +310,54 @@ public class FileUtility
return FileType.Unknown;
}
public IDictionary<Accessability, bool> GetAccessability(string fileName)
{
var result = new Dictionary<Accessability, bool>();
foreach (var r in Enum.GetValues<Accessability>())
{
var val = false;
switch (r)
{
case Accessability.ImageView:
val = CanImageView(fileName);
break;
case Accessability.MediaView:
val = CanMediaView(fileName);
break;
case Accessability.WebView:
val = CanWebView(fileName);
break;
case Accessability.WebEdit:
val = CanWebEdit(fileName);
break;
case Accessability.WebReview:
val = CanWebReview(fileName);
break;
case Accessability.WebCustomFilterEditing:
val = CanWebCustomFilterEditing(fileName);
break;
case Accessability.WebRestrictedEditing:
val = CanWebRestrictedEditing(fileName);
break;
case Accessability.WebComment:
val = CanWebComment(fileName);
break;
case Accessability.CoAuhtoring:
val = CanCoAuhtoring(fileName);
break;
case Accessability.Convert:
val = CanConvert(fileName);
break;
}
result.Add(r, val);
}
return result;
}
public bool CanImageView(string fileName)
{
var ext = GetFileExtension(fileName);
@ -350,6 +412,13 @@ public class FileUtility
return ExtsCoAuthoring.Exists(r => r.Equals(ext, StringComparison.OrdinalIgnoreCase));
}
public bool CanConvert(string fileName)
{
var ext = GetFileExtension(fileName);
return ExtsMustConvert.Exists(r => r.Equals(ext, StringComparison.OrdinalIgnoreCase));
}
public bool CanIndex(string fileName)
{
var ext = GetFileExtension(fileName);

View File

@ -564,6 +564,24 @@ namespace ASC.Files.Core.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to copy the file.
/// </summary>
public static string ErrorMassage_SecurityException_CopyFile {
get {
return ResourceManager.GetString("ErrorMassage_SecurityException_CopyFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to copy the folder.
/// </summary>
public static string ErrorMassage_SecurityException_CopyFolder {
get {
return ResourceManager.GetString("ErrorMassage_SecurityException_CopyFolder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to create.
/// </summary>
@ -753,6 +771,24 @@ namespace ASC.Files.Core.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have permission to copy to this folder.
/// </summary>
public static string ErrorMessage_SecurityException_CopyToFolder {
get {
return ResourceManager.GetString("ErrorMessage_SecurityException_CopyToFolder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have permission to move to this folder.
/// </summary>
public static string ErrorMessage_SecurityException_MoveToFolder {
get {
return ResourceManager.GetString("ErrorMessage_SecurityException_MoveToFolder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough permission to archive the room.
/// </summary>
@ -771,6 +807,15 @@ namespace ASC.Files.Core.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to You can&apos;t pin a room.
/// </summary>
public static string ErrorrMessage_PinRoom {
get {
return ResourceManager.GetString("ErrorrMessage_PinRoom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Everyone.
/// </summary>

View File

@ -285,6 +285,12 @@
<data name="ErrorMassage_SecurityException_Auth" xml:space="preserve">
<value>Can't authorize at {0} provider with given credentials.</value>
</data>
<data name="ErrorMassage_SecurityException_CopyFile" xml:space="preserve">
<value>You don't have enough permission to copy the file</value>
</data>
<data name="ErrorMassage_SecurityException_CopyFolder" xml:space="preserve">
<value>You don't have enough permission to copy the folder</value>
</data>
<data name="ErrorMassage_SecurityException_Create" xml:space="preserve">
<value>You don't have enough permission to create</value>
</data>
@ -348,12 +354,21 @@
<data name="ErrorMessage_InvintationLink" xml:space="preserve">
<value>The invitation link is invalid or it's validity has expired</value>
</data>
<data name="ErrorMessage_SecurityException_CopyToFolder" xml:space="preserve">
<value>You don't have permission to copy to this folder</value>
</data>
<data name="ErrorMessage_SecurityException_MoveToFolder" xml:space="preserve">
<value>You don't have permission to move to this folder</value>
</data>
<data name="ErrorMessage_UnarchiveRoom" xml:space="preserve">
<value>You don't have enough permission to archive the room</value>
</data>
<data name="ErrorMessage_UpdateArchivedRoom" xml:space="preserve">
<value>You cannot edit archived rooms</value>
</data>
<data name="ErrorrMessage_PinRoom" xml:space="preserve">
<value>You can't pin a room</value>
</data>
<data name="Everyone" xml:space="preserve">
<value>Everyone</value>
</data>

View File

@ -106,7 +106,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
}
private async Task DeleteFoldersAsync(IEnumerable<T> folderIds, IServiceScope scope, bool isNeedSendActions = false)
private async Task DeleteFoldersAsync(IEnumerable<T> folderIds, IServiceScope scope, bool isNeedSendActions = false, bool checkPermissions = true)
{
var scopeClass = scope.ServiceProvider.GetService<FileDeleteOperationScope>();
var (fileMarker, filesMessageService, roomLogoManager) = scopeClass;
@ -127,7 +127,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_DeleteFolder;
}
else if (!_ignoreException && !await FilesSecurity.CanDeleteAsync(folder))
else if (!_ignoreException && checkPermissions && !await FilesSecurity.CanDeleteAsync(folder))
{
canCalculate = FolderDao.CanCalculateSubitems(folderId) ? default : folderId;
@ -135,6 +135,8 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
else
{
checkPermissions = isRoom ? false : checkPermissions;
canCalculate = FolderDao.CanCalculateSubitems(folderId) ? default : folderId;
await fileMarker.RemoveMarkAsNewForAllAsync(folder);
@ -148,7 +150,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
if (providerInfo.FolderId != null)
{
await roomLogoManager.DeleteAsync(providerInfo.FolderId);
await roomLogoManager.DeleteAsync(providerInfo.FolderId, checkPermissions);
}
}
@ -167,16 +169,16 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
if (immediately && FolderDao.UseRecursiveOperation(folder.Id, default(T)))
{
var files = await FileDao.GetFilesAsync(folder.Id).ToListAsync();
await DeleteFilesAsync(files, scope);
await DeleteFilesAsync(files, scope, checkPermissions: checkPermissions);
var folders = await FolderDao.GetFoldersAsync(folder.Id).ToListAsync();
await DeleteFoldersAsync(folders.Select(f => f.Id).ToList(), scope);
await DeleteFoldersAsync(folders.Select(f => f.Id).ToList(), scope, checkPermissions: checkPermissions);
if (await FolderDao.IsEmptyAsync(folder.Id))
{
if (isRoom)
{
await roomLogoManager.DeleteAsync(folder.Id);
await roomLogoManager.DeleteAsync(folder.Id, checkPermissions);
}
await FolderDao.DeleteFolderAsync(folder.Id);
@ -194,7 +196,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
else
{
var files = await FileDao.GetFilesAsync(folder.Id, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true).ToListAsync();
var (isError, message) = await WithErrorAsync(scope, files, true);
var (isError, message) = await WithErrorAsync(scope, files, true, checkPermissions);
if (!_ignoreException && isError)
{
this[Err] = message;
@ -205,7 +207,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
{
if (isRoom)
{
await roomLogoManager.DeleteAsync(folder.Id);
await roomLogoManager.DeleteAsync(folder.Id, checkPermissions);
}
await FolderDao.DeleteFolderAsync(folder.Id);
@ -238,7 +240,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
}
private async Task DeleteFilesAsync(IEnumerable<T> fileIds, IServiceScope scope, bool isNeedSendActions = false)
private async Task DeleteFilesAsync(IEnumerable<T> fileIds, IServiceScope scope, bool isNeedSendActions = false, bool checkPermissions = true)
{
var scopeClass = scope.ServiceProvider.GetService<FileDeleteOperationScope>();
var socketManager = scope.ServiceProvider.GetService<SocketManager>();
@ -249,7 +251,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
CancellationToken.ThrowIfCancellationRequested();
var file = await FileDao.GetFileAsync(fileId);
var (isError, message) = await WithErrorAsync(scope, new[] { file }, false);
var (isError, message) = await WithErrorAsync(scope, new[] { file }, false, checkPermissions);
if (file == null)
{
this[Err] = FilesCommonResource.ErrorMassage_FileNotFound;
@ -316,7 +318,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
}
private async Task<(bool isError, string message)> WithErrorAsync(IServiceScope scope, IEnumerable<File<T>> files, bool folder)
private async Task<(bool isError, string message)> WithErrorAsync(IServiceScope scope, IEnumerable<File<T>> files, bool folder, bool checkPermissions)
{
var entryManager = scope.ServiceProvider.GetService<EntryManager>();
var fileTracker = scope.ServiceProvider.GetService<FileTrackerHelper>();
@ -324,13 +326,13 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
string error = null;
foreach (var file in files)
{
if (!await FilesSecurity.CanDeleteAsync(file))
if (checkPermissions && !await FilesSecurity.CanDeleteAsync(file))
{
error = FilesCommonResource.ErrorMassage_SecurityException_DeleteFile;
return (true, error);
}
if (await entryManager.FileLockedForMeAsync(file.Id))
if (checkPermissions && await entryManager.FileLockedForMeAsync(file.Id))
{
error = FilesCommonResource.ErrorMassage_LockedFile;

View File

@ -113,6 +113,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
var fileMarker = scope.ServiceProvider.GetService<FileMarker>();
var folderDao = scope.ServiceProvider.GetService<IFolderDao<TTo>>();
var fileSecurity = scope.ServiceProvider.GetService<FileSecurity>();
this[Res] += string.Format("folder_{0}{1}", _daoFolderId, SplitChar);
@ -139,41 +140,53 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
return;
}
if (_copy)
if (0 < Folders.Count)
{
Folder<T> rootFrom = null;
if (0 < Folders.Count)
{
rootFrom = await FolderDao.GetRootFolderAsync(Folders[0]);
}
var firstFolder = await FolderDao.GetFolderAsync(Folders[0]);
if (0 < Files.Count)
if (_copy && !await FilesSecurity.CanCopyAsync(firstFolder))
{
rootFrom = await FolderDao.GetRootFolderByFileAsync(Files[0]);
}
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_CopyFolder;
if (rootFrom != null)
return;
}
else if (!_copy && !await FilesSecurity.CanMoveAsync(firstFolder))
{
if (rootFrom.FolderType == FolderType.TRASH)
{
throw new InvalidOperationException("Can not copy from Trash.");
}
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
if (rootFrom.FolderType == FolderType.Archive)
{
throw new InvalidOperationException("Can not copy from Archive.");
}
return;
}
}
if (toFolder.RootFolderType == FolderType.TRASH)
if (0 < Files.Count)
{
var firstFile = await FileDao.GetFileAsync(Files[0]);
if (_copy && !await FilesSecurity.CanCopyAsync(firstFile))
{
throw new InvalidOperationException("Can not copy to Trash.");
}
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_CopyFile;
if (toFolder.RootFolderType == FolderType.Archive)
{
throw new InvalidOperationException("Can not copy to Archive");
return;
}
else if (!_copy && !await FilesSecurity.CanMoveAsync(firstFile))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
return;
}
}
if (_copy && !await fileSecurity.CanCopyToAsync(toFolder))
{
this[Err] = FilesCommonResource.ErrorMessage_SecurityException_CopyToFolder;
return;
}
else if (!_copy && !await fileSecurity.CanMoveToAsync(toFolder))
{
this[Err] = FilesCommonResource.ErrorMessage_SecurityException_MoveToFolder;
return;
}
var needToMark = new List<FileEntry<TTo>>();
@ -191,7 +204,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
}
private async Task<List<FileEntry<TTo>>> MoveOrCopyFoldersAsync<TTo>(IServiceScope scope, List<T> folderIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toFolderParents)
private async Task<List<FileEntry<TTo>>> MoveOrCopyFoldersAsync<TTo>(IServiceScope scope, List<T> folderIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toFolderParents, bool checkPermissions = true)
{
var needToMark = new List<FileEntry<TTo>>();
@ -215,7 +228,6 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
CancellationToken.ThrowIfCancellationRequested();
var folder = await FolderDao.GetFolderAsync(folderId);
var (isError, message) = await WithErrorAsync(scope, await FileDao.GetFilesAsync(folder.Id, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true).ToListAsync());
var isRoom = DocSpaceHelper.IsRoom(folder.FolderType);
@ -223,9 +235,13 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
this[Err] = FilesCommonResource.ErrorMassage_FolderNotFound;
}
else if (!await FilesSecurity.CanReadAsync(folder))
else if (copy && checkPermissions && !await FilesSecurity.CanCopyAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_ReadFolder;
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_CopyFolder;
}
else if (!copy && checkPermissions && !await FilesSecurity.CanMoveAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
else if (!isRoom && (toFolder.FolderType == FolderType.VirtualRooms || toFolder.RootFolderType == FolderType.Archive))
{
@ -239,7 +255,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
else if (!await FilesSecurity.CanDownloadAsync(folder))
else if (checkPermissions && !await FilesSecurity.CanDownloadAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException;
}
@ -250,6 +266,11 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
else if (!Equals(folder.ParentId ?? default, toFolderId) || _resolveType == FileConflictResolveType.Duplicate)
{
checkPermissions = isRoom ? false : checkPermissions;
var files = await FileDao.GetFilesAsync(folder.Id, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true).ToListAsync();
var (isError, message) = await WithErrorAsync(scope, files, checkPermissions);
try
{
//if destination folder contains folder with same name then merge folders
@ -288,12 +309,12 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
if (toFolder.ProviderId == folder.ProviderId // crossDao operation is always recursive
&& FolderDao.UseRecursiveOperation(folder.Id, toFolderId))
{
await MoveOrCopyFilesAsync(scope, await FileDao.GetFilesAsync(folder.Id).ToListAsync(), newFolder, copy, toFolderParents);
await MoveOrCopyFoldersAsync(scope, await FolderDao.GetFoldersAsync(folder.Id).Select(f => f.Id).ToListAsync(), newFolder, copy, toFolderParents);
await MoveOrCopyFilesAsync(scope, await FileDao.GetFilesAsync(folder.Id).ToListAsync(), newFolder, copy, toFolderParents, checkPermissions);
await MoveOrCopyFoldersAsync(scope, await FolderDao.GetFoldersAsync(folder.Id).Select(f => f.Id).ToListAsync(), newFolder, copy, toFolderParents, checkPermissions);
if (!copy)
{
if (!await FilesSecurity.CanDeleteAsync(folder))
if (checkPermissions && !await FilesSecurity.CanMoveAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
@ -329,7 +350,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
sb.Append($"folder_{newFolderId}{SplitChar}");
}
}
else if (!await FilesSecurity.CanDeleteAsync(folder))
else if (checkPermissions && !await FilesSecurity.CanMoveAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
@ -360,7 +381,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
else
{
if (!await FilesSecurity.CanDeleteAsync(folder))
if (checkPermissions && !await FilesSecurity.CanMoveAsync(folder))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder;
}
@ -453,7 +474,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
return needToMark;
}
private async Task<List<FileEntry<TTo>>> MoveOrCopyFilesAsync<TTo>(IServiceScope scope, List<T> fileIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toParentFolders)
private async Task<List<FileEntry<TTo>>> MoveOrCopyFilesAsync<TTo>(IServiceScope scope, List<T> fileIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toParentFolders, bool checkPermissions = true)
{
var needToMark = new List<FileEntry<TTo>>();
@ -475,7 +496,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
CancellationToken.ThrowIfCancellationRequested();
var file = await FileDao.GetFileAsync(fileId);
var (isError, message) = await WithErrorAsync(scope, new[] { file });
var (isError, message) = await WithErrorAsync(scope, new[] { file }, checkPermissions);
if (file == null)
{
@ -485,11 +506,15 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
}
else if (!await FilesSecurity.CanReadAsync(file))
else if (copy && !await FilesSecurity.CanCopyAsync(file))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_ReadFile;
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_CopyFile;
}
else if (!await FilesSecurity.CanDownloadAsync(file))
else if (!copy && checkPermissions && !await FilesSecurity.CanMoveAsync(file))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
}
else if (checkPermissions && !await FilesSecurity.CanDownloadAsync(file))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException;
}
@ -597,7 +622,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
{
if (_resolveType == FileConflictResolveType.Overwrite)
{
if (!await FilesSecurity.CanEditAsync(conflict))
if (checkPermissions && !await FilesSecurity.CanEditAsync(conflict))
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException;
}
@ -710,20 +735,20 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
return needToMark;
}
private async Task<(bool isError, string message)> WithErrorAsync(IServiceScope scope, IEnumerable<File<T>> files)
private async Task<(bool isError, string message)> WithErrorAsync(IServiceScope scope, IEnumerable<File<T>> files, bool checkPermissions = true)
{
var entryManager = scope.ServiceProvider.GetService<EntryManager>();
var fileTracker = scope.ServiceProvider.GetService<FileTrackerHelper>();
string error = null;
foreach (var file in files)
{
if (!await FilesSecurity.CanDeleteAsync(file))
if(checkPermissions && !await FilesSecurity.CanMoveAsync(file))
{
error = FilesCommonResource.ErrorMassage_SecurityException_MoveFile;
return (true, error);
}
if (await entryManager.FileLockedForMeAsync(file.Id))
if (checkPermissions && await entryManager.FileLockedForMeAsync(file.Id))
{
error = FilesCommonResource.ErrorMassage_LockedFile;

View File

@ -41,6 +41,7 @@ public class AceWrapper : IMapFrom<RoomInvitation>
public string Email { get; set; }
public SubjectType SubjectType { get; set; }
public FileShareOptions FileShareOptions { get; set; }
public bool CanEditAccess { get; set; }
[JsonPropertyName("title")]
public string SubjectName { get; set; }

View File

@ -216,7 +216,7 @@ public class EntryStatusManager
var tagDao = _daoFactory.GetTagDao<T>();
var tagsTask = tagDao.GetTagsAsync(_authContext.CurrentAccount.ID, new[] { TagType.Favorite, TagType.Template, TagType.Locked }, files);
var tagsTask = tagDao.GetTagsAsync(TagType.Locked, files).ToDictionaryAsync(k => k.EntryId, v => v);
var tagsNewTask = tagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, files).ToListAsync();
var tags = await tagsTask;
@ -224,34 +224,15 @@ public class EntryStatusManager
foreach (var file in files)
{
foreach (var t in tags)
if (tags.TryGetValue(file.Id, out var lockedTag))
{
if (!t.Key.Equals(file.Id))
{
continue;
}
var lockedBy = lockedTag.Owner;
file.Locked = lockedBy != Guid.Empty;
file.LockedBy = lockedBy != Guid.Empty && lockedBy != _authContext.CurrentAccount.ID
? _global.GetUserName(lockedBy)
: null;
if (t.Value.Any(r => r.Type == TagType.Favorite))
{
file.IsFavorite = true;
}
if (t.Value.Any(r => r.Type == TagType.Template))
{
file.IsTemplate = true;
}
var lockedTag = t.Value.FirstOrDefault(r => r.Type == TagType.Locked);
if (lockedTag != null)
{
var lockedBy = lockedTag.Owner;
file.Locked = lockedBy != Guid.Empty;
file.LockedBy = lockedBy != Guid.Empty && lockedBy != _authContext.CurrentAccount.ID
? _global.GetUserName(lockedBy)
: null;
continue;
}
continue;
}
if (tagsNew.Any(r => r.EntryId.Equals(file.Id)))

View File

@ -113,7 +113,8 @@ public class FileSharingAceHelper<T>
foreach (var w in aceWrappers.OrderByDescending(ace => ace.SubjectGroup))
{
if (entry is Folder<T> folder && DocSpaceHelper.IsRoom(folder.FolderType) && !DocSpaceHelper.ValidateShare(folder.FolderType, w.Access, _userManager.IsUser(w.Id)))
if (entry is Folder<T> folder && DocSpaceHelper.IsRoom(folder.FolderType) &&
!DocSpaceHelper.ValidateShare(folder.FolderType, w.Access, _userManager.IsUser(w.Id)))
{
continue;
}
@ -343,12 +344,7 @@ public class FileSharingHelper
return true;
}
if (entry.RootFolderType == FolderType.VirtualRooms && (_global.IsDocSpaceAdministrator || await _fileSecurity.CanShareAsync(entry)))
{
return true;
}
if (folder != null && DocSpaceHelper.IsRoom(folder.FolderType) && folder.RootFolderType != FolderType.Archive && await _fileSecurity.CanEditRoomAsync(entry))
if (await _fileSecurity.CanEditAccessAsync(entry))
{
return true;
}
@ -445,6 +441,7 @@ public class FileSharing
var result = new List<AceWrapper>();
var shares = await _fileSecurity.GetSharesAsync(entry);
var isRoom = entry is Folder<T> { Private: false } room && DocSpaceHelper.IsRoom(room.FolderType);
var canEditAccess = await _fileSecurity.CanEditAccessAsync(entry);
var records = shares
.GroupBy(r => r.Subject)
@ -499,13 +496,16 @@ public class FileSharing
Id = r.Subject,
SubjectGroup = isgroup,
Access = share,
FileShareOptions = r.FileShareOptions
FileShareOptions = r.FileShareOptions,
};
w.CanEditAccess = _authContext.CurrentAccount.ID != w.Id && w.SubjectType == SubjectType.UserOrGroup && canEditAccess;
if (isRoom && r.IsLink)
{
w.Link = _roomLinkService.GetInvitationLink(r.Subject, r.Owner);
w.SubjectGroup = true;
w.CanEditAccess = false;
}
else
{
@ -560,7 +560,8 @@ public class FileSharing
SubjectName = _global.GetUserName(ownerId),
SubjectGroup = false,
Access = FileShare.ReadWrite,
Owner = true
Owner = true,
CanEditAccess = false,
};
result.Add(w);