Merge branch 'develop' into bugfix/rooms
This commit is contained in:
commit
0f72136ff0
11
build/install/docker/.dockerignore
Normal file
11
build/install/docker/.dockerignore
Normal file
@ -0,0 +1,11 @@
|
||||
node_modules
|
||||
bin
|
||||
.yarn
|
||||
.git
|
||||
.vscode
|
||||
.github
|
||||
Logs
|
||||
Data
|
||||
TestsResults
|
||||
i18next
|
||||
*.bat
|
@ -34,6 +34,7 @@ RUN apt-get -y update && \
|
||||
apt-get install -y nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ADD https://api.github.com/repos/ONLYOFFICE/DocSpace/git/refs/heads/${GIT_BRANCH} version.json
|
||||
RUN echo ${GIT_BRANCH} && \
|
||||
git clone --recurse-submodules -b ${GIT_BRANCH} https://github.com/ONLYOFFICE/DocSpace.git ${SRC_PATH}
|
||||
|
||||
|
@ -19,3 +19,5 @@ echo "Stop all backend containers"
|
||||
docker stop $(docker ps -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all backend containers"
|
||||
docker rm -f $(docker ps -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $1}')
|
||||
echo "Remove all backend images"
|
||||
docker rmi -f $(docker images -a | egrep "onlyoffice" | egrep -v "mysql|rabbitmq|redis|elasticsearch|documentserver" | awk 'NR>0 {print $3}')
|
@ -46,11 +46,11 @@ public class EmployeeFullDto : EmployeeDto
|
||||
public string AvatarMax { get; set; }
|
||||
public string AvatarMedium { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public bool IsDocSpaceAdmin { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
public bool IsLDAP { get; set; }
|
||||
public List<string> ListAdminModules { get; set; }
|
||||
public bool IsOwner { get; set; }
|
||||
public bool IsUser { get; set; }
|
||||
public bool IsVisitor { get; set; }
|
||||
public string CultureName { get; set; }
|
||||
public string MobilePhone { get; set; }
|
||||
public MobilePhoneActivationStatus MobilePhoneActivationStatus { get; set; }
|
||||
@ -70,7 +70,7 @@ public class EmployeeFullDto : EmployeeDto
|
||||
Email = "my@gmail.com",
|
||||
FirstName = "Mike",
|
||||
Id = Guid.Empty,
|
||||
IsDocSpaceAdmin = false,
|
||||
IsAdmin = false,
|
||||
ListAdminModules = new List<string> { "projects", "crm" },
|
||||
UserName = "Mike.Zanyatski",
|
||||
LastName = "Zanyatski",
|
||||
@ -190,8 +190,8 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
|
||||
Terminated = _apiDateTimeHelper.Get(userInfo.TerminatedDate),
|
||||
WorkFrom = _apiDateTimeHelper.Get(userInfo.WorkFromDate),
|
||||
Email = userInfo.Email,
|
||||
IsUser = _userManager.IsUser(userInfo),
|
||||
IsDocSpaceAdmin = _userManager.IsDocSpaceAdmin(userInfo),
|
||||
IsVisitor = _userManager.IsUser(userInfo),
|
||||
IsAdmin = _userManager.IsDocSpaceAdmin(userInfo),
|
||||
IsOwner = userInfo.IsOwner(_context.Tenant),
|
||||
IsLDAP = userInfo.IsLDAP(),
|
||||
IsSSO = userInfo.IsSSO()
|
||||
|
@ -62,9 +62,9 @@ public class DocSpaceLinkHelper
|
||||
return _signature.Create(linkId);
|
||||
}
|
||||
|
||||
public string MakeKey(string email)
|
||||
public string MakeKey(string email, EmployeeType employeeType)
|
||||
{
|
||||
return email + ConfirmType.LinkInvite.ToStringFast() + EmployeeType.RoomAdmin.ToStringFast();
|
||||
return email + ConfirmType.LinkInvite.ToStringFast() + employeeType.ToStringFast();
|
||||
}
|
||||
|
||||
public Guid Parse(string key)
|
||||
@ -72,14 +72,14 @@ public class DocSpaceLinkHelper
|
||||
return _signature.Read<Guid>(key);
|
||||
}
|
||||
|
||||
public ValidationResult Validate(string key, string email)
|
||||
public ValidationResult Validate(string key, string email, EmployeeType employeeType)
|
||||
{
|
||||
return string.IsNullOrEmpty(email) ? ValidateExternalLink(key) : ValidateEmailLink(email, key);
|
||||
return string.IsNullOrEmpty(email) ? ValidateRoomExternalLink(key) : ValidateEmailLink(email, key, employeeType);
|
||||
}
|
||||
|
||||
private ValidationResult ValidateEmailLink(string email, string key)
|
||||
public ValidationResult ValidateEmailLink(string email, string key, EmployeeType employeeType)
|
||||
{
|
||||
var result = _emailValidationKeyProvider.ValidateEmailKey(MakeKey(email), key, ExpirationInterval);
|
||||
var result = _emailValidationKeyProvider.ValidateEmailKey(MakeKey(email, employeeType), key, ExpirationInterval);
|
||||
|
||||
if (result == ValidationResult.Ok)
|
||||
{
|
||||
@ -94,16 +94,16 @@ public class DocSpaceLinkHelper
|
||||
return result;
|
||||
}
|
||||
|
||||
private ValidationResult ValidateExternalLink(string key)
|
||||
public ValidationResult ValidateRoomExternalLink(string key)
|
||||
{
|
||||
var payload = Parse(key);
|
||||
|
||||
if (payload == default)
|
||||
{
|
||||
return ValidationResult.Invalid;
|
||||
return payload == default ? ValidationResult.Invalid : ValidationResult.Ok;
|
||||
}
|
||||
|
||||
return ValidationResult.Ok;
|
||||
public ValidationResult ValidateExtarnalLink(string key, EmployeeType employeeType)
|
||||
{
|
||||
return _emailValidationKeyProvider.ValidateEmailKey(ConfirmType.LinkInvite.ToStringFast() + (int)employeeType, key);
|
||||
}
|
||||
|
||||
private bool CanUsed(string email, string key, TimeSpan interval)
|
||||
|
@ -110,11 +110,12 @@ public class EmailValidationKeyModelHelper
|
||||
break;
|
||||
|
||||
case ConfirmType.LinkInvite:
|
||||
checkKeyResult = _docSpaceLinkHelper.Validate(key, email);
|
||||
checkKeyResult = string.IsNullOrEmpty(email) ? _docSpaceLinkHelper.ValidateRoomExternalLink(key)
|
||||
: _docSpaceLinkHelper.ValidateEmailLink(email, key, emplType ?? default);
|
||||
|
||||
if (checkKeyResult == ValidationResult.Invalid)
|
||||
{
|
||||
checkKeyResult = _provider.ValidateEmailKey(type.ToString() + (int)emplType, key, _provider.ValidEmailKeyInterval);
|
||||
checkKeyResult = _provider.ValidateEmailKey(type.ToString() + (int)(emplType ?? default), key, _provider.ValidEmailKeyInterval);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -60,7 +60,7 @@ public class UserServiceCache
|
||||
CacheGroupCacheItem = cacheGroupCacheItem;
|
||||
CacheUserGroupRefItem = cacheUserGroupRefItem;
|
||||
|
||||
cacheUserInfoItem.Subscribe(InvalidateCache, CacheNotifyAction.Any);
|
||||
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), CacheNotifyAction.Any);
|
||||
cacheUserPhotoItem.Subscribe((p) => Cache.Remove(p.Key), CacheNotifyAction.Remove);
|
||||
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(g), CacheNotifyAction.Any);
|
||||
|
||||
@ -72,7 +72,7 @@ public class UserServiceCache
|
||||
{
|
||||
if (userInfo != null)
|
||||
{
|
||||
var key = GetUserCacheKey(userInfo.Tenant, new Guid(userInfo.Id));
|
||||
var key = GetUserCacheKey(userInfo.Tenant);
|
||||
Cache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
@ -36,5 +36,6 @@ public enum EmployeeType
|
||||
{
|
||||
All = 0,
|
||||
RoomAdmin = 1,
|
||||
User = 2
|
||||
User = 2,
|
||||
DocSpaceAdmin = 3,
|
||||
}
|
||||
|
@ -90,6 +90,11 @@ public class DisplayUserSettingsHelper
|
||||
}
|
||||
var result = _userFormatter.GetUserName(userInfo, format);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
result = userInfo.Email;
|
||||
}
|
||||
|
||||
return withHtmlEncode ? HtmlEncode(result) : result;
|
||||
}
|
||||
public string HtmlEncode(string str)
|
||||
|
@ -12,5 +12,6 @@
|
||||
"Add": "Add",
|
||||
"Invited": "Invited",
|
||||
"EmailErrorMessage": "Email address not valid. You can edit the email by clicking on it.",
|
||||
"СhooseFromList": "Сhoose from list"
|
||||
"СhooseFromList": "Сhoose from list",
|
||||
"InviteUsers": "Invite users"
|
||||
}
|
||||
|
@ -46,16 +46,15 @@
|
||||
"Remove": "Remove",
|
||||
"RoleCommentator": "Commentator",
|
||||
"RoleCommentatorDescription": "Operations with existing files: viewing, commenting.",
|
||||
"RoleDocSpaceAdminDescription": "DocSpace admins can access DocSpace settings, manage and archive rooms, invite new users and assign roles below their level. All admins have access to the Personal section.",
|
||||
"RoleEditor": "Editor",
|
||||
"RoleEditorDescription": "Operations with existing files: viewing, editing, form filling, reviewing, commenting.",
|
||||
"RoleFormFiller": "Form filler",
|
||||
"RoleFormFillerDescription": "Operations with existing files: viewing, form filling, reviewing, commenting.",
|
||||
"RoleReviewer": "Reviewer",
|
||||
"RoleReviewerDescription": "Operations with existing files: viewing, reviewing, commenting.",
|
||||
"RoleRoomAdmin": "Room admin",
|
||||
"RoleRoomAdminDescription": "Room admins can create and manage the assigned rooms, invite new users and assign roles below their level. All admins have access to the Personal section.",
|
||||
"RoleDocSpaceAdmin": "DocSpace admin",
|
||||
"RoleDocSpaceAdminDescription": "DocSpace admins can access DocSpace settings, manage and archive rooms, invite new users and assign roles below their level. All admins have access to the Personal section.",
|
||||
"RoleUserDescription": "Users can only access the rooms they are invited to by admins. They can't create own rooms, folders or files.",
|
||||
"RoleViewer": "Viewer",
|
||||
"RoleViewerDescription": "File viewing",
|
||||
"Spreadsheets": "Spreadsheets",
|
||||
|
@ -6,9 +6,6 @@ import CatalogItem from "@docspace/components/catalog-item";
|
||||
import { FolderType, ShareAccessRights } from "@docspace/common/constants";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import DragAndDrop from "@docspace/components/drag-and-drop";
|
||||
import withLoader from "../../../HOCs/withLoader";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import Loader from "@docspace/components/loader";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
const StyledDragAndDrop = styled(DragAndDrop)`
|
||||
@ -34,7 +31,7 @@ const Item = ({
|
||||
labelBadge,
|
||||
iconBadge,
|
||||
}) => {
|
||||
const [isDragActive, setIsDragActive] = React.useState(false);
|
||||
const [isDragActive, setIsDragActive] = useState(false);
|
||||
|
||||
const isDragging = dragging ? showDragItems(item) : false;
|
||||
|
||||
@ -125,6 +122,7 @@ const Items = ({
|
||||
data,
|
||||
showText,
|
||||
pathParts,
|
||||
rootFolderType,
|
||||
selectedTreeNode,
|
||||
onClick,
|
||||
onBadgeClick,
|
||||
@ -161,6 +159,13 @@ const Items = ({
|
||||
if (selectedTreeNode.length > 0) {
|
||||
const isMainFolder = dataMainTree.indexOf(selectedTreeNode[0]) !== -1;
|
||||
|
||||
if (
|
||||
rootFolderType === FolderType.Rooms &&
|
||||
item.rootFolderType === FolderType.Rooms
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pathParts && pathParts.includes(item.id) && !isMainFolder)
|
||||
return true;
|
||||
|
||||
@ -173,7 +178,7 @@ const Items = ({
|
||||
return `${item.id}` === selectedTreeNode[0];
|
||||
}
|
||||
},
|
||||
[selectedTreeNode, pathParts, docSpace]
|
||||
[selectedTreeNode, pathParts, docSpace, rootFolderType]
|
||||
);
|
||||
const getEndOfBlock = React.useCallback(
|
||||
(item) => {
|
||||
@ -348,7 +353,7 @@ const Items = ({
|
||||
);
|
||||
|
||||
if (isVisitor) {
|
||||
items.length > 1 && items.splice(1, 0, filesHeader);
|
||||
items.length > 1 && items.splice(2, 0, filesHeader);
|
||||
} else {
|
||||
items.splice(3, 0, filesHeader);
|
||||
}
|
||||
@ -417,7 +422,7 @@ export default inject(
|
||||
isPrivacyFolder,
|
||||
} = treeFoldersStore;
|
||||
|
||||
const { id } = selectedFolderStore;
|
||||
const { id, pathParts, rootFolderType } = selectedFolderStore;
|
||||
const { moveDragItems, uploadEmptyFolders } = filesActionsStore;
|
||||
const { setEmptyTrashDialogVisible } = dialogsStore;
|
||||
|
||||
@ -430,7 +435,7 @@ export default inject(
|
||||
currentId: id,
|
||||
showText: auth.settingsStore.showText,
|
||||
docSpace: auth.settingsStore.docSpace,
|
||||
pathParts: selectedFolderStore.pathParts,
|
||||
pathParts,
|
||||
data: treeFolders,
|
||||
selectedTreeNode,
|
||||
draggableItems: dragging ? selection : null,
|
||||
@ -442,6 +447,7 @@ export default inject(
|
||||
uploadEmptyFolders,
|
||||
setEmptyTrashDialogVisible,
|
||||
trashIsEmpty,
|
||||
rootFolderType,
|
||||
};
|
||||
}
|
||||
)(withTranslation(["Files", "Common", "Translations"])(observer(Items)));
|
||||
|
@ -14,7 +14,7 @@ import MobileView from "./MobileView";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
import config from "PACKAGE_FILE";
|
||||
import withLoader from "../../../HOCs/withLoader";
|
||||
import { Events } from "@docspace/common/constants";
|
||||
import { Events, EmployeeType } from "@docspace/common/constants";
|
||||
import { getMainButtonItems } from "SRC_DIR/helpers/plugins";
|
||||
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
@ -91,6 +91,8 @@ const ArticleMainButtonContent = (props) => {
|
||||
isOwner,
|
||||
isAdmin,
|
||||
isVisitor,
|
||||
|
||||
setInvitePanelOptions,
|
||||
} = props;
|
||||
|
||||
const isAccountsPage = selectedTreeNode[0] === "accounts";
|
||||
@ -171,8 +173,13 @@ const ArticleMainButtonContent = (props) => {
|
||||
|
||||
const onInvite = React.useCallback((e) => {
|
||||
const type = e.action;
|
||||
toastr.warning("Work in progress " + type);
|
||||
console.log("invite ", type);
|
||||
|
||||
setInvitePanelOptions({
|
||||
visible: true,
|
||||
roomId: -1,
|
||||
hideSelector: true,
|
||||
defaultAccess: type,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onInviteAgain = React.useCallback(() => {
|
||||
@ -265,7 +272,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
icon: "/static/images/person.admin.react.svg",
|
||||
label: t("Common:DocSpaceAdmin"),
|
||||
onClick: onInvite,
|
||||
action: "administrator",
|
||||
action: EmployeeType.Admin,
|
||||
key: "administrator",
|
||||
},
|
||||
{
|
||||
@ -274,7 +281,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
icon: "/static/images/person.manager.react.svg",
|
||||
label: t("Common:RoomAdmin"),
|
||||
onClick: onInvite,
|
||||
action: "manager",
|
||||
action: EmployeeType.User,
|
||||
key: "manager",
|
||||
},
|
||||
{
|
||||
@ -283,7 +290,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
icon: "/static/images/person.user.react.svg",
|
||||
label: t("Common:User"),
|
||||
onClick: onInvite,
|
||||
action: "user",
|
||||
action: EmployeeType.Guest,
|
||||
key: "user",
|
||||
},
|
||||
]
|
||||
@ -492,7 +499,7 @@ export default inject(
|
||||
selectedTreeNode,
|
||||
} = treeFoldersStore;
|
||||
const { startUpload } = uploadDataStore;
|
||||
const { setSelectFileDialogVisible } = dialogsStore;
|
||||
const { setSelectFileDialogVisible, setInvitePanelOptions } = dialogsStore;
|
||||
|
||||
const isArticleLoading = (!isLoaded || isLoading) && firstLoad;
|
||||
|
||||
@ -522,6 +529,7 @@ export default inject(
|
||||
startUpload,
|
||||
|
||||
setSelectFileDialogVisible,
|
||||
setInvitePanelOptions,
|
||||
|
||||
isLoading,
|
||||
isLoaded,
|
||||
|
@ -59,9 +59,10 @@ const RootFolderContainer = (props) => {
|
||||
const trashDescription = t("TrashEmptyDescription");
|
||||
const favoritesDescription = t("FavoritesEmptyContainerDescription");
|
||||
const recentDescription = t("RecentEmptyContainerDescription");
|
||||
|
||||
const roomsDescription = isVisitor
|
||||
? t("RoomEmptyContainerDescription")
|
||||
: t("RoomEmptyContainerDescriptionUser");
|
||||
? t("RoomEmptyContainerDescriptionUser")
|
||||
: t("RoomEmptyContainerDescription");
|
||||
const archiveRoomsDescription = t("ArchiveEmptyScreen");
|
||||
|
||||
const privateRoomHeader = t("PrivateRoomHeader");
|
||||
|
@ -29,13 +29,19 @@ const InvitePanel = ({
|
||||
visible,
|
||||
setRoomSecurity,
|
||||
getRoomSecurityInfo,
|
||||
getPortalInviteLinks,
|
||||
userLink,
|
||||
guestLink,
|
||||
adminLink,
|
||||
defaultAccess,
|
||||
inviteUsers,
|
||||
}) => {
|
||||
const [selectedRoom, setSelectedRoom] = useState(null);
|
||||
const [hasErrors, setHasErrors] = useState(false);
|
||||
const [shareLinks, setShareLinks] = useState([]);
|
||||
const [roomUsers, setRoomUsers] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const selectRoom = () => {
|
||||
const room = folders.find((folder) => folder.id === roomId);
|
||||
|
||||
if (room) {
|
||||
@ -45,7 +51,9 @@ const InvitePanel = ({
|
||||
setSelectedRoom(info);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getInfo = () => {
|
||||
getRoomSecurityInfo(roomId).then((users) => {
|
||||
let links = [];
|
||||
|
||||
@ -58,6 +66,7 @@ const InvitePanel = ({
|
||||
title,
|
||||
shareLink,
|
||||
expirationDate,
|
||||
access: defaultAccess,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -65,7 +74,39 @@ const InvitePanel = ({
|
||||
setShareLinks(links);
|
||||
setRoomUsers(users);
|
||||
});
|
||||
}, [roomId]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (roomId === -1) {
|
||||
if (!userLink || !guestLink || !adminLink) getPortalInviteLinks();
|
||||
|
||||
setShareLinks([
|
||||
{
|
||||
id: "user",
|
||||
title: "User",
|
||||
shareLink: userLink,
|
||||
access: 1,
|
||||
},
|
||||
{
|
||||
id: "guest",
|
||||
title: "Guest",
|
||||
shareLink: guestLink,
|
||||
access: 2,
|
||||
},
|
||||
{
|
||||
id: "admin",
|
||||
title: "Admin",
|
||||
shareLink: adminLink,
|
||||
access: 3,
|
||||
},
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
selectRoom();
|
||||
getInfo();
|
||||
}, [roomId, userLink, guestLink, adminLink]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasErrors = inviteItems.some((item) => !!item.errors?.length);
|
||||
@ -74,7 +115,11 @@ const InvitePanel = ({
|
||||
}, [inviteItems]);
|
||||
|
||||
const onClose = () => {
|
||||
setInvitePanelOptions({ visible: false });
|
||||
setInvitePanelOptions({
|
||||
visible: false,
|
||||
hideSelector: false,
|
||||
defaultAccess: 1,
|
||||
});
|
||||
setInviteItems([]);
|
||||
};
|
||||
|
||||
@ -88,7 +133,11 @@ const InvitePanel = ({
|
||||
|
||||
const onClickSend = async (e) => {
|
||||
const invitations = inviteItems.map((item) => {
|
||||
let newItem = { access: item.access };
|
||||
let newItem = {};
|
||||
|
||||
roomId === -1
|
||||
? (newItem.type = item.access)
|
||||
: (newItem.access = item.access);
|
||||
|
||||
item.avatar ? (newItem.id = item.id) : (newItem.email = item.email);
|
||||
|
||||
@ -97,20 +146,25 @@ const InvitePanel = ({
|
||||
|
||||
const data = {
|
||||
invitations,
|
||||
notify: true,
|
||||
message: "Invitation message",
|
||||
};
|
||||
|
||||
if (roomId !== -1) {
|
||||
data.notify = true;
|
||||
data.message = "Invitation message";
|
||||
}
|
||||
|
||||
try {
|
||||
await setRoomSecurity(roomId, data);
|
||||
roomId === -1
|
||||
? await inviteUsers(data)
|
||||
: await setRoomSecurity(roomId, data);
|
||||
onClose();
|
||||
toastr.success(`Users invited to ${selectedRoom.title}`);
|
||||
toastr.success(`Users invited`);
|
||||
} catch (err) {
|
||||
toastr.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const roomType = selectedRoom ? selectedRoom.roomType : 5;
|
||||
const roomType = selectedRoom ? selectedRoom.roomType : -1;
|
||||
|
||||
return (
|
||||
<StyledInvitePanel>
|
||||
@ -127,7 +181,9 @@ const InvitePanel = ({
|
||||
withoutBodyScroll
|
||||
>
|
||||
<StyledBlock>
|
||||
<StyledHeading>{t("InviteUsersToRoom")}</StyledHeading>
|
||||
<StyledHeading>
|
||||
{roomId === -1 ? t("InviteUsers") : t("InviteUsersToRoom")}
|
||||
</StyledHeading>
|
||||
</StyledBlock>
|
||||
|
||||
<ExternalLinks t={t} shareLinks={shareLinks} roomType={roomType} />
|
||||
@ -168,7 +224,14 @@ const InvitePanel = ({
|
||||
export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
|
||||
const { theme } = auth.settingsStore;
|
||||
|
||||
const { getUsersByQuery } = peopleStore.usersStore;
|
||||
const { getUsersByQuery, inviteUsers } = peopleStore.usersStore;
|
||||
|
||||
const {
|
||||
getPortalInviteLinks,
|
||||
userLink,
|
||||
guestLink,
|
||||
adminLink,
|
||||
} = peopleStore.inviteLinksStore;
|
||||
|
||||
const {
|
||||
inviteItems,
|
||||
@ -195,7 +258,13 @@ export default inject(({ auth, peopleStore, filesStore, dialogsStore }) => {
|
||||
setRoomSecurity,
|
||||
theme,
|
||||
visible: invitePanelOptions.visible,
|
||||
defaultAccess: invitePanelOptions.defaultAccess,
|
||||
getFolderInfo,
|
||||
getPortalInviteLinks,
|
||||
userLink,
|
||||
guestLink,
|
||||
adminLink,
|
||||
inviteUsers,
|
||||
};
|
||||
})(
|
||||
withTranslation([
|
||||
|
@ -12,10 +12,11 @@ const AccessSelector = ({
|
||||
defaultAccess,
|
||||
}) => {
|
||||
const width = containerRef?.current?.offsetWidth - 32;
|
||||
|
||||
const accessOptions = getAccessOptions(t, roomType, false, true);
|
||||
|
||||
const selectedOption = accessOptions.filter(
|
||||
(access) => access.access === defaultAccess
|
||||
(access) => access.access === +defaultAccess
|
||||
)[0];
|
||||
|
||||
return (
|
||||
|
@ -22,25 +22,45 @@ import {
|
||||
|
||||
const ExternalLinks = ({
|
||||
t,
|
||||
hideSelector,
|
||||
roomId,
|
||||
roomType,
|
||||
defaultAccess,
|
||||
shareLinks,
|
||||
setInvitationLinks,
|
||||
}) => {
|
||||
const [linksVisible, setLinksVisible] = useState(false);
|
||||
const [actionLinksVisible, setActionLinksVisible] = useState(false);
|
||||
const [activeLink, setActiveLink] = useState({});
|
||||
|
||||
const inputsRef = useRef();
|
||||
|
||||
const toggleLinks = (e) => {
|
||||
if (roomId === -1) {
|
||||
const link = shareLinks.find((l) => l.access === +defaultAccess);
|
||||
|
||||
setActiveLink(link);
|
||||
} else {
|
||||
setInvitationLinks(roomId, shareLinks[0].id, "Invite", +defaultAccess);
|
||||
|
||||
setActiveLink(shareLinks[0]);
|
||||
}
|
||||
|
||||
setLinksVisible(!linksVisible);
|
||||
|
||||
if (!linksVisible) copyLink(shareLinks[0].shareLink);
|
||||
if (!linksVisible) copyLink(activeLink.shareLink);
|
||||
};
|
||||
|
||||
const onSelectAccess = (access) => {
|
||||
console.log(access);
|
||||
if (roomId === -1) {
|
||||
const link = shareLinks.find((l) => l.access === access.access);
|
||||
|
||||
setActiveLink(link);
|
||||
} else {
|
||||
setInvitationLinks(roomId, shareLinks[0].id, "Invite", +access.access);
|
||||
|
||||
setActiveLink(shareLinks[0]);
|
||||
}
|
||||
copyLink(activeLink.shareLink);
|
||||
};
|
||||
|
||||
const copyLink = (link) => {
|
||||
@ -73,7 +93,7 @@ const ExternalLinks = ({
|
||||
|
||||
closeActionLinks();
|
||||
},
|
||||
[closeActionLinks, links, t]
|
||||
[closeActionLinks, t]
|
||||
);
|
||||
|
||||
const shareTwitter = useCallback(
|
||||
@ -90,39 +110,9 @@ const ExternalLinks = ({
|
||||
|
||||
closeActionLinks();
|
||||
},
|
||||
[closeActionLinks, links]
|
||||
[closeActionLinks]
|
||||
);
|
||||
|
||||
const links =
|
||||
!!shareLinks.length &&
|
||||
shareLinks?.map((link) => {
|
||||
return (
|
||||
<StyledInviteInputContainer key={link.id}>
|
||||
<StyledInviteInput>
|
||||
<InputBlock
|
||||
scale
|
||||
value={link.shareLink}
|
||||
isReadOnly
|
||||
iconName="/static/images/copy.react.svg"
|
||||
onIconClick={() => copyLink(link.shareLink)}
|
||||
hoverColor="#333333"
|
||||
iconColor="#A3A9AE"
|
||||
/>
|
||||
</StyledInviteInput>
|
||||
|
||||
{!hideSelector && (
|
||||
<AccessSelector
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={defaultAccess}
|
||||
onSelectAccess={onSelectAccess}
|
||||
containerRef={inputsRef}
|
||||
/>
|
||||
)}
|
||||
</StyledInviteInputContainer>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledBlock noPadding ref={inputsRef}>
|
||||
<StyledSubHeader inline>
|
||||
@ -156,17 +146,41 @@ const ExternalLinks = ({
|
||||
)}
|
||||
<StyledToggleButton isChecked={linksVisible} onChange={toggleLinks} />
|
||||
</StyledSubHeader>
|
||||
{linksVisible && links}
|
||||
{linksVisible && (
|
||||
<StyledInviteInputContainer key={activeLink.id}>
|
||||
<StyledInviteInput>
|
||||
<InputBlock
|
||||
scale
|
||||
value={activeLink.shareLink}
|
||||
isReadOnly
|
||||
iconName="/static/images/copy.react.svg"
|
||||
onIconClick={() => copyLink(activeLink.shareLink)}
|
||||
hoverColor="#333333"
|
||||
iconColor="#A3A9AE"
|
||||
/>
|
||||
</StyledInviteInput>
|
||||
<AccessSelector
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={activeLink.access}
|
||||
onSelectAccess={onSelectAccess}
|
||||
containerRef={inputsRef}
|
||||
/>
|
||||
</StyledInviteInputContainer>
|
||||
)}
|
||||
</StyledBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ dialogsStore, filesStore }) => {
|
||||
const { invitePanelOptions } = dialogsStore;
|
||||
const { setInvitationLinks } = filesStore;
|
||||
const { roomId, hideSelector, defaultAccess } = invitePanelOptions;
|
||||
|
||||
return {
|
||||
roomId: invitePanelOptions.roomId,
|
||||
hideSelector: invitePanelOptions.hideSelector,
|
||||
defaultAccess: invitePanelOptions.defaultAccess,
|
||||
setInvitationLinks,
|
||||
roomId,
|
||||
hideSelector,
|
||||
defaultAccess,
|
||||
};
|
||||
})(observer(ExternalLinks));
|
||||
|
@ -82,6 +82,8 @@ const InviteInput = ({
|
||||
setUsersList(users);
|
||||
} else {
|
||||
closeInviteInputPanel();
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -125,6 +127,8 @@ const InviteInput = ({
|
||||
const items = removeExist([item, ...inviteItems]);
|
||||
setInviteItems(items);
|
||||
closeInviteInputPanel();
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -157,6 +161,8 @@ const InviteInput = ({
|
||||
|
||||
setInviteItems(filtered);
|
||||
closeInviteInputPanel();
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
};
|
||||
|
||||
const addItems = (users) => {
|
||||
@ -166,6 +172,8 @@ const InviteInput = ({
|
||||
|
||||
setInviteItems(filtered);
|
||||
closeInviteInputPanel();
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
};
|
||||
|
||||
const dropDownMaxHeight = usersList.length > 5 ? { maxHeight: 240 } : {};
|
||||
@ -186,9 +194,6 @@ const InviteInput = ({
|
||||
const closeInviteInputPanel = (e) => {
|
||||
if (e?.target.tagName.toUpperCase() == "INPUT") return;
|
||||
|
||||
setInputValue("");
|
||||
setUsersList([]);
|
||||
|
||||
setSearchPanelVisible(false);
|
||||
};
|
||||
|
||||
@ -215,6 +220,7 @@ const InviteInput = ({
|
||||
<>
|
||||
<StyledSubHeader>
|
||||
{t("IndividualInvitation")}
|
||||
{!hideSelector && (
|
||||
<StyledLink
|
||||
fontWeight="600"
|
||||
type="action"
|
||||
@ -223,6 +229,7 @@ const InviteInput = ({
|
||||
>
|
||||
{t("СhooseFromList")}
|
||||
</StyledLink>
|
||||
)}
|
||||
</StyledSubHeader>
|
||||
|
||||
<StyledInviteInputContainer ref={inputsRef}>
|
||||
@ -258,17 +265,15 @@ const InviteInput = ({
|
||||
)}
|
||||
</StyledDropDown>
|
||||
|
||||
{!hideSelector && (
|
||||
<AccessSelector
|
||||
t={t}
|
||||
roomType={roomType}
|
||||
defaultAccess={defaultAccess}
|
||||
defaultAccess={selectedAccess}
|
||||
onSelectAccess={onSelectAccess}
|
||||
containerRef={inputsRef}
|
||||
/>
|
||||
)}
|
||||
|
||||
{addUsersPanelVisible && (
|
||||
{!hideSelector && addUsersPanelVisible && (
|
||||
<AddUsersPanel
|
||||
onParentPanelClose={onClose}
|
||||
onClose={closeUsersPanel}
|
||||
|
@ -36,7 +36,7 @@ const Item = ({
|
||||
|
||||
const accesses = getAccessOptions(t, roomType, true);
|
||||
|
||||
const defaultAccess = accesses.find((option) => option.access === access);
|
||||
const defaultAccess = accesses.find((option) => option.access === +access);
|
||||
|
||||
const errorsInList = () => {
|
||||
const hasErrors = inviteItems.some((item) => !!item.errors?.length);
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { ShareAccessRights, RoomsType } from "@docspace/common/constants";
|
||||
import {
|
||||
ShareAccessRights,
|
||||
RoomsType,
|
||||
EmployeeType,
|
||||
} from "@docspace/common/constants";
|
||||
|
||||
export const getAccessOptions = (
|
||||
t,
|
||||
@ -10,19 +14,27 @@ export const getAccessOptions = (
|
||||
const accesses = {
|
||||
docSpaceAdmin: {
|
||||
key: "docSpaceAdmin",
|
||||
label: t("Translations:RoleDocSpaceAdmin"),
|
||||
label: t("Common:DocSpaceAdmin"),
|
||||
description: t("Translations:RoleDocSpaceAdminDescription"),
|
||||
quota: t("Common:Paid"),
|
||||
color: "#EDC409",
|
||||
access: ShareAccessRights.FullAccess,
|
||||
access:
|
||||
roomType === -1 ? EmployeeType.Admin : ShareAccessRights.FullAccess,
|
||||
},
|
||||
roomAdmin: {
|
||||
key: "roomAdmin",
|
||||
label: t("Translations:RoleRoomAdmin"),
|
||||
label: t("Common:RoomAdmin"),
|
||||
description: t("Translations:RoleRoomAdminDescription"),
|
||||
quota: t("Common:Paid"),
|
||||
color: "#EDC409",
|
||||
access: ShareAccessRights.RoomManager,
|
||||
access:
|
||||
roomType === -1 ? EmployeeType.User : ShareAccessRights.RoomManager,
|
||||
},
|
||||
user: {
|
||||
key: "user",
|
||||
label: t("Common:User"),
|
||||
description: t("Translations:RoleUserDescription"),
|
||||
access: EmployeeType.Guest,
|
||||
},
|
||||
editor: {
|
||||
key: "editor",
|
||||
@ -100,6 +112,14 @@ export const getAccessOptions = (
|
||||
accesses.viewer,
|
||||
];
|
||||
break;
|
||||
case -1:
|
||||
options = [
|
||||
accesses.docSpaceAdmin,
|
||||
accesses.roomAdmin,
|
||||
{ key: "s1", isSeparator: withSeparator },
|
||||
accesses.user,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
const removeOption = [
|
||||
|
@ -77,7 +77,6 @@ const Dialogs = ({
|
||||
{...data}
|
||||
/>
|
||||
)}
|
||||
|
||||
{changeUserStatusDialogVisible && (
|
||||
<ChangeUserStatusDialog
|
||||
visible={changeUserStatusDialogVisible}
|
||||
@ -85,14 +84,12 @@ const Dialogs = ({
|
||||
{...data}
|
||||
/>
|
||||
)}
|
||||
|
||||
{sendInviteDialogVisible && (
|
||||
<SendInviteDialog
|
||||
visible={sendInviteDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
/>
|
||||
)}
|
||||
|
||||
{deleteDialogVisible && (
|
||||
<DeleteUsersDialog
|
||||
visible={deleteDialogVisible}
|
||||
|
@ -20,6 +20,7 @@ import { Base } from "@docspace/components/themes";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import withPeopleLoader from "SRC_DIR/HOCs/withPeopleLoader";
|
||||
import { EmployeeType } from "@docspace/common/constants";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -163,6 +164,7 @@ const SectionHeaderContent = (props) => {
|
||||
isInfoPanelVisible,
|
||||
isOwner,
|
||||
isAdmin,
|
||||
setInvitePanelOptions,
|
||||
} = props;
|
||||
|
||||
//console.log("SectionHeaderContent render");
|
||||
@ -202,9 +204,14 @@ const SectionHeaderContent = (props) => {
|
||||
const headerMenu = getHeaderMenu(t);
|
||||
|
||||
const onInvite = React.useCallback((e) => {
|
||||
const type = e.target.dataset.action;
|
||||
toastr.warning("Work in progress " + type);
|
||||
console.log("invite ", type);
|
||||
const type = e.target.dataset.type;
|
||||
|
||||
setInvitePanelOptions({
|
||||
visible: true,
|
||||
roomId: -1,
|
||||
hideSelector: true,
|
||||
defaultAccess: type,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onInviteAgain = React.useCallback(() => {
|
||||
@ -224,7 +231,7 @@ const SectionHeaderContent = (props) => {
|
||||
icon: "/static/images/person.admin.react.svg",
|
||||
label: t("Common:DocSpaceAdmin"),
|
||||
onClick: onInvite,
|
||||
"data-action": "administrator",
|
||||
"data-type": EmployeeType.Admin,
|
||||
key: "administrator",
|
||||
},
|
||||
{
|
||||
@ -233,7 +240,7 @@ const SectionHeaderContent = (props) => {
|
||||
icon: "/static/images/person.manager.react.svg",
|
||||
label: t("Common:RoomAdmin"),
|
||||
onClick: onInvite,
|
||||
"data-action": "manager",
|
||||
"data-type": EmployeeType.User,
|
||||
key: "manager",
|
||||
},
|
||||
{
|
||||
@ -242,7 +249,7 @@ const SectionHeaderContent = (props) => {
|
||||
icon: "/static/images/person.user.react.svg",
|
||||
label: t("Common:User"),
|
||||
onClick: onInvite,
|
||||
"data-action": "user",
|
||||
"data-type": EmployeeType.Guest,
|
||||
key: "user",
|
||||
},
|
||||
{
|
||||
@ -323,12 +330,14 @@ const SectionHeaderContent = (props) => {
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
inject(({ auth, peopleStore }) => {
|
||||
inject(({ auth, peopleStore, dialogsStore }) => {
|
||||
const {
|
||||
setIsVisible: setInfoPanelIsVisible,
|
||||
isVisible: isInfoPanelVisible,
|
||||
} = auth.infoPanelStore;
|
||||
|
||||
const { setInvitePanelOptions } = dialogsStore;
|
||||
|
||||
const { isOwner, isAdmin } = auth.userStore.user;
|
||||
|
||||
const { selectionStore, headerMenuStore, getHeaderMenu } = peopleStore;
|
||||
@ -355,6 +364,7 @@ export default withRouter(
|
||||
isInfoPanelVisible,
|
||||
isOwner,
|
||||
isAdmin,
|
||||
setInvitePanelOptions,
|
||||
};
|
||||
})(
|
||||
withTranslation([
|
||||
|
@ -218,13 +218,15 @@ const RegisterContainer = styled.div`
|
||||
`;
|
||||
|
||||
const Confirm = (props) => {
|
||||
const { settings, t, greetingTitle, providers, isDesktop } = props;
|
||||
const { settings, t, greetingTitle, providers, isDesktop, linkData } = props;
|
||||
const inputRef = React.useRef(null);
|
||||
|
||||
const emailFromLink = linkData.email ? linkData.email : "";
|
||||
|
||||
const [moreAuthVisible, setMoreAuthVisible] = useState(false);
|
||||
const [ssoLabel, setSsoLabel] = useState("");
|
||||
const [ssoUrl, setSsoUrl] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [email, setEmail] = useState(emailFromLink);
|
||||
const [emailValid, setEmailValid] = useState(true);
|
||||
const [emailErrorText, setEmailErrorText] = useState("");
|
||||
|
||||
@ -286,7 +288,7 @@ const Confirm = (props) => {
|
||||
|
||||
const onSubmit = () => {
|
||||
const { defaultPage, linkData, hashSettings } = props;
|
||||
const isVisitor = parseInt(linkData.emplType) === 2;
|
||||
const type = parseInt(linkData.emplType);
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
@ -335,13 +337,17 @@ const Confirm = (props) => {
|
||||
email: email,
|
||||
};
|
||||
|
||||
const registerData = Object.assign(personalData, {
|
||||
isVisitor: isVisitor,
|
||||
});
|
||||
if (!!type) {
|
||||
personalData.type = type;
|
||||
}
|
||||
|
||||
const key = props.linkData.confirmHeader;
|
||||
if (!!linkData.key) {
|
||||
personalData.key = linkData.key;
|
||||
}
|
||||
|
||||
createConfirmUser(registerData, loginData, key)
|
||||
const headerKey = linkData.confirmHeader;
|
||||
|
||||
createConfirmUser(personalData, loginData, headerKey)
|
||||
.then(() => window.location.replace(defaultPage))
|
||||
.catch((error) => {
|
||||
console.error("confirm error", error);
|
||||
@ -637,7 +643,7 @@ const Confirm = (props) => {
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
isDisabled={isLoading || !!emailFromLink}
|
||||
autoComplete="username"
|
||||
onChange={onChangeEmail}
|
||||
onBlur={onBlurEmail}
|
||||
|
@ -97,11 +97,6 @@ const FilesSection = React.memo(() => {
|
||||
"/rooms/personal",
|
||||
"/rooms/personal/filter",
|
||||
|
||||
"/rooms/archived",
|
||||
"/rooms/archived/filter",
|
||||
"/rooms/archived/:room",
|
||||
"/rooms/archived/:room/filter",
|
||||
|
||||
"/files/trash",
|
||||
"/files/trash/filter",
|
||||
]}
|
||||
@ -115,6 +110,11 @@ const FilesSection = React.memo(() => {
|
||||
"/rooms/shared/:room",
|
||||
"/rooms/shared/:room/filter",
|
||||
|
||||
"/rooms/archived",
|
||||
"/rooms/archived/filter",
|
||||
"/rooms/archived/:room",
|
||||
"/rooms/archived/:room/filter",
|
||||
|
||||
"/files/favorite",
|
||||
"/files/favorite/filter",
|
||||
|
||||
|
@ -388,7 +388,13 @@ const MainContainer = styled.div`
|
||||
|
||||
const StyledCard = styled.div`
|
||||
display: grid;
|
||||
|
||||
${({ isSingle }) =>
|
||||
!isSingle &&
|
||||
css`
|
||||
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
|
||||
`};
|
||||
|
||||
height: ${({ cardHeight }) => `${cardHeight}px`};
|
||||
`;
|
||||
|
||||
|
@ -1,17 +1,22 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import InfiniteLoaderComponent from "@docspace/components/infinite-loader";
|
||||
import { StyledCard, StyledItem } from "../StyledTileView";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import uniqueid from "lodash/uniqueId";
|
||||
|
||||
const Card = ({ children, ...rest }) => {
|
||||
const Card = ({ children, countTilesInRow, ...rest }) => {
|
||||
const horizontalGap = 16;
|
||||
const fileHeight = 220 + horizontalGap;
|
||||
const cardHeight = fileHeight;
|
||||
|
||||
return (
|
||||
<StyledCard className="Card" cardHeight={cardHeight} {...rest}>
|
||||
<StyledCard
|
||||
className="Card"
|
||||
cardHeight={cardHeight}
|
||||
isSingle={countTilesInRow}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</StyledCard>
|
||||
);
|
||||
@ -37,7 +42,7 @@ const InfiniteGrid = (props) => {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const countTilesInRow = getCountTilesInRow();
|
||||
const [countTilesInRow, setCountTilesInRow] = useState(getCountTilesInRow());
|
||||
|
||||
let cards = [];
|
||||
const list = [];
|
||||
@ -51,6 +56,24 @@ const InfiniteGrid = (props) => {
|
||||
if (clear) cards = [];
|
||||
};
|
||||
|
||||
const setTilesCount = () => {
|
||||
const newCount = getCountTilesInRow();
|
||||
if (countTilesInRow !== newCount) setCountTilesInRow(newCount);
|
||||
};
|
||||
|
||||
const onResize = () => {
|
||||
setTilesCount();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTilesCount();
|
||||
window.addEventListener("resize", onResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", onResize);
|
||||
};
|
||||
});
|
||||
|
||||
React.Children.map(children, (child) => {
|
||||
if (child) {
|
||||
if (cards.length && cards.length === countTilesInRow) {
|
||||
@ -59,7 +82,11 @@ const InfiniteGrid = (props) => {
|
||||
}
|
||||
|
||||
const cardKey = uniqueid("card-item_");
|
||||
cards.push(<Card key={cardKey}>{child}</Card>);
|
||||
cards.push(
|
||||
<Card countTilesInRow={countTilesInRow} key={cardKey}>
|
||||
{child}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -67,6 +67,7 @@ const InfoPanelBodyContent = ({
|
||||
isAdmin: props.isAdmin,
|
||||
getRoomMembers: props.getRoomMembers,
|
||||
changeUserType: props.changeUserType,
|
||||
setInvitePanelOptions: props.setInvitePanelOptions,
|
||||
},
|
||||
historyProps: {
|
||||
selectedFolder: selectedFolder,
|
||||
@ -234,7 +235,7 @@ export default inject(
|
||||
const { getIcon, getFolderIcon } = settingsStore;
|
||||
const { onSelectItem, openLocationAction } = filesActionsStore;
|
||||
const { changeType: changeUserType } = peopleStore;
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
const { setSharingPanelVisible, setInvitePanelOptions } = dialogsStore;
|
||||
const { isRootFolder } = selectedFolderStore;
|
||||
const { gallerySelected } = oformsStore;
|
||||
const {
|
||||
@ -331,6 +332,7 @@ export default inject(
|
||||
getRoomHistory,
|
||||
getFileHistory,
|
||||
setSharingPanelVisible,
|
||||
setInvitePanelOptions,
|
||||
|
||||
getIcon,
|
||||
getFolderIcon,
|
||||
|
@ -6,6 +6,8 @@ import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
import { StyledUserList, StyledUserTypeHeader } from "../../styles/members";
|
||||
|
||||
import { ShareAccessRights } from "@docspace/common/constants";
|
||||
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import User from "./User";
|
||||
@ -23,6 +25,7 @@ const Members = ({
|
||||
|
||||
getRoomMembers,
|
||||
changeUserType,
|
||||
setInvitePanelOptions,
|
||||
}) => {
|
||||
const [members, setMembers] = useState(null);
|
||||
const [showLoader, setShowLoader] = useState(false);
|
||||
@ -65,7 +68,12 @@ const Members = ({
|
||||
}, [selection]);
|
||||
|
||||
const onAddUsers = () => {
|
||||
toastr.warning("Work in progress");
|
||||
setInvitePanelOptions({
|
||||
visible: true,
|
||||
roomId: selection.id,
|
||||
hideSelector: false,
|
||||
defaultAccess: ShareAccessRights.ReadOnly,
|
||||
});
|
||||
};
|
||||
|
||||
if (showLoader) return <Loaders.InfoPanelViewLoader view="members" />;
|
||||
|
@ -13,7 +13,7 @@ const HeaderItem = ({ children, className, ...rest }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Card = ({ children, ...rest }) => {
|
||||
const Card = ({ children, countTilesInRow, ...rest }) => {
|
||||
const getItemSize = (child) => {
|
||||
const isFile = child?.props?.className?.includes("file");
|
||||
const isFolder = child?.props?.className?.includes("folder");
|
||||
@ -37,7 +37,12 @@ const Card = ({ children, ...rest }) => {
|
||||
const cardHeight = getItemSize(children);
|
||||
|
||||
return (
|
||||
<StyledCard className="Card" cardHeight={cardHeight} {...rest}>
|
||||
<StyledCard
|
||||
isSingle={countTilesInRow}
|
||||
className="Card"
|
||||
cardHeight={cardHeight}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</StyledCard>
|
||||
);
|
||||
@ -143,7 +148,11 @@ const InfiniteGrid = (props) => {
|
||||
}
|
||||
|
||||
const cardKey = uniqueid("card-item_");
|
||||
cards.push(<Card key={cardKey}>{child}</Card>);
|
||||
cards.push(
|
||||
<Card countTilesInRow={countTilesInRow} key={cardKey}>
|
||||
{child}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -14,7 +14,11 @@ const paddingCss = css`
|
||||
|
||||
const StyledCard = styled.div`
|
||||
display: grid;
|
||||
${({ isSingle }) =>
|
||||
!isSingle &&
|
||||
css`
|
||||
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
|
||||
`};
|
||||
height: ${({ cardHeight }) => `${cardHeight}px`};
|
||||
`;
|
||||
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
hideLoader,
|
||||
frameCallbackData,
|
||||
frameCallCommand,
|
||||
getObjectByLocation,
|
||||
} from "@docspace/common/utils";
|
||||
import FilesFilter from "@docspace/common/api/files/filter";
|
||||
import { getGroup } from "@docspace/common/api/groups";
|
||||
@ -80,10 +81,13 @@ class PureHome extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRoomFolder = getObjectByLocation(window.location)?.folder;
|
||||
|
||||
if (
|
||||
categoryType == CategoryType.Shared ||
|
||||
(categoryType == CategoryType.Shared ||
|
||||
categoryType == CategoryType.SharedRoom ||
|
||||
categoryType == CategoryType.Archive
|
||||
categoryType == CategoryType.Archive) &&
|
||||
!isRoomFolder
|
||||
) {
|
||||
filterObj = RoomsFilter.getFilter(window.location);
|
||||
|
||||
|
@ -419,7 +419,7 @@ class ContextOptionsStore {
|
||||
const { isGracePeriod } = this.authStore.currentTariffStatusStore;
|
||||
const { isFreeTariff } = this.authStore.currentQuotaStore;
|
||||
|
||||
if (isGracePeriod || isFreeTariff) {
|
||||
if (isGracePeriod) {
|
||||
this.dialogsStore.setInviteUsersWarningDialogVisible(true);
|
||||
} else {
|
||||
this.dialogsStore.setInvitePanelOptions({
|
||||
|
@ -2694,8 +2694,8 @@ class FilesStore {
|
||||
return Math.floor(sectionWidth / minTileWidth);
|
||||
};
|
||||
|
||||
setInvitationLinks = async (id, linkId, title, access) => {
|
||||
return await api.rooms.setInvitationLinks(id, linkId, title, access);
|
||||
setInvitationLinks = async (roomId, linkId, title, access) => {
|
||||
return await api.rooms.setInvitationLinks(roomId, linkId, title, access);
|
||||
};
|
||||
|
||||
resendEmailInvitations = async (id, usersIds) => {
|
||||
|
@ -8,6 +8,7 @@ class InviteLinksStore {
|
||||
peopleStore = null;
|
||||
userLink = null;
|
||||
guestLink = null;
|
||||
adminLink = null;
|
||||
|
||||
constructor(peopleStore) {
|
||||
this.peopleStore = peopleStore;
|
||||
@ -17,18 +18,25 @@ class InviteLinksStore {
|
||||
setUserLink = (link) => {
|
||||
this.userLink = link;
|
||||
};
|
||||
|
||||
setGuestLink = (link) => {
|
||||
this.guestLink = link;
|
||||
};
|
||||
|
||||
setAdminLink = (link) => {
|
||||
this.adminLink = link;
|
||||
};
|
||||
|
||||
getPortalInviteLinks = async () => {
|
||||
const isViewerAdmin = this.peopleStore.authStore.isAdmin;
|
||||
|
||||
if (!isViewerAdmin) return Promise.resolve();
|
||||
|
||||
const links = await getInvitationLinks();
|
||||
|
||||
this.setUserLink(links.userLink);
|
||||
this.setGuestLink(links.guestLink);
|
||||
this.setAdminLink(links.adminLink);
|
||||
};
|
||||
|
||||
getShortenedLink = async (link, forUser = false) => {
|
||||
|
@ -402,6 +402,12 @@ class UsersStore {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
inviteUsers = async (data) => {
|
||||
const result = await api.people.inviteUsers(data);
|
||||
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
}
|
||||
|
||||
export default UsersStore;
|
||||
|
@ -195,6 +195,18 @@ export function getUserById(userId) {
|
||||
});
|
||||
}
|
||||
|
||||
export const inviteUsers = async (data) => {
|
||||
const options = {
|
||||
method: "post",
|
||||
url: "/people/invite",
|
||||
data,
|
||||
};
|
||||
|
||||
const res = await request(options);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export function resendUserInvites(userIds) {
|
||||
return request({
|
||||
method: "put",
|
||||
|
@ -13,7 +13,7 @@ const USER_INVITE_LINK = "userInvitationLink";
|
||||
const INVITE_LINK_TTL = "localStorageLinkTtl";
|
||||
const LINKS_TTL = 6 * 3600 * 1000;
|
||||
|
||||
export function getInvitationLink(isGuest) {
|
||||
export function getInvitationLink(type) {
|
||||
const curLinksTtl = localStorage.getItem(INVITE_LINK_TTL);
|
||||
const now = +new Date();
|
||||
|
||||
@ -26,30 +26,40 @@ export function getInvitationLink(isGuest) {
|
||||
}
|
||||
|
||||
const link = localStorage.getItem(
|
||||
isGuest ? GUEST_INVITE_LINK : USER_INVITE_LINK
|
||||
type === 2 ? GUEST_INVITE_LINK : USER_INVITE_LINK
|
||||
);
|
||||
|
||||
return link
|
||||
return link && type !== 3
|
||||
? Promise.resolve(link)
|
||||
: request({
|
||||
method: "get",
|
||||
url: `/portal/users/invite/${isGuest ? 2 : 1}.json`,
|
||||
url: `/portal/users/invite/${type}.json`,
|
||||
}).then((link) => {
|
||||
if (type !== 3) {
|
||||
localStorage.setItem(
|
||||
isGuest ? GUEST_INVITE_LINK : USER_INVITE_LINK,
|
||||
type === 2 ? GUEST_INVITE_LINK : USER_INVITE_LINK,
|
||||
link
|
||||
);
|
||||
}
|
||||
return Promise.resolve(link);
|
||||
});
|
||||
}
|
||||
|
||||
export function getInvitationLinks() {
|
||||
const isGuest = true;
|
||||
return Promise.all([getInvitationLink(), getInvitationLink(isGuest)]).then(
|
||||
([userInvitationLinkResp, guestInvitationLinkResp]) => {
|
||||
return Promise.all([
|
||||
getInvitationLink(1),
|
||||
getInvitationLink(2),
|
||||
getInvitationLink(3),
|
||||
]).then(
|
||||
([
|
||||
userInvitationLinkResp,
|
||||
guestInvitationLinkResp,
|
||||
adminInvitationLinkResp,
|
||||
]) => {
|
||||
return Promise.resolve({
|
||||
userLink: userInvitationLinkResp,
|
||||
guestLink: guestInvitationLinkResp,
|
||||
adminLink: adminInvitationLinkResp,
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -266,4 +276,5 @@ export function sendPaymentRequest(email, userName, message) {
|
||||
userName,
|
||||
message,
|
||||
},
|
||||
})};
|
||||
});
|
||||
}
|
||||
|
@ -245,10 +245,10 @@ export function removeLogoFromRoom(id) {
|
||||
});
|
||||
}
|
||||
|
||||
export const setInvitationLinks = async (id, linkId, title, access) => {
|
||||
export const setInvitationLinks = async (roomId, linkId, title, access) => {
|
||||
const options = {
|
||||
method: "put",
|
||||
url: `/files/rooms/${id}/links`,
|
||||
url: `/files/rooms/${roomId}/links`,
|
||||
data: {
|
||||
linkId,
|
||||
title,
|
||||
|
@ -23,6 +23,8 @@ const StyledArticle = styled.article`
|
||||
|
||||
//padding: 0 20px;
|
||||
|
||||
border-right: ${(props) => props.theme.catalog.verticalLine};
|
||||
|
||||
@media ${tablet} {
|
||||
min-width: ${(props) => (props.showText ? "243px" : "60px")};
|
||||
max-width: ${(props) => (props.showText ? "243px" : "60px")};
|
||||
@ -63,6 +65,8 @@ const StyledArticle = styled.article`
|
||||
padding: 0;
|
||||
top: ${(props) => (props.isBannerVisible ? "-16px" : "64px")} !important;
|
||||
height: calc(100% - 64px) !important;
|
||||
|
||||
border-right: none;
|
||||
`}
|
||||
|
||||
z-index: ${(props) =>
|
||||
@ -77,7 +81,7 @@ const StyledArticle = styled.article`
|
||||
.scroll-body {
|
||||
overflow-x: hidden !important;
|
||||
height: calc(100% - 200px);
|
||||
padding: 0 20px;
|
||||
padding: 0 20px !important;
|
||||
|
||||
@media ${tablet} {
|
||||
height: calc(100% - 150px);
|
||||
@ -278,16 +282,19 @@ const StyledArticleProfile = styled.div`
|
||||
justify-content: center;
|
||||
|
||||
border-top: ${(props) => props.theme.catalog.profile.borderTop};
|
||||
border-right: ${(props) => props.theme.catalog.verticalLine}
|
||||
background-color: ${(props) => props.theme.catalog.profile.background};
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 16px 14px;
|
||||
}
|
||||
|
||||
${isTablet &&
|
||||
${
|
||||
isTablet &&
|
||||
css`
|
||||
padding: 16px 14px;
|
||||
`}
|
||||
`
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
cursor: pointer;
|
||||
|
@ -27,6 +27,7 @@ export const EmployeeStatus = Object.freeze({
|
||||
export const EmployeeType = Object.freeze({
|
||||
User: 1,
|
||||
Guest: 2,
|
||||
Admin: 3,
|
||||
UserString: "user",
|
||||
RoomAdmin: "manager",
|
||||
DocSpaceAdmin: "admin",
|
||||
|
@ -21,6 +21,10 @@ const AccessRightSelect = ({
|
||||
}) => {
|
||||
const [currentItem, setCurrentItem] = useState(selectedOption);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentItem(selectedOption);
|
||||
}, [selectedOption]);
|
||||
|
||||
const onSelectCurrentItem = useCallback(
|
||||
(e) => {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
|
@ -1858,6 +1858,8 @@ const Base = {
|
||||
|
||||
headerBurgerColor: "#657077",
|
||||
|
||||
verticalLine: "1px solid #eceef1",
|
||||
|
||||
profile: {
|
||||
borderTop: "1px solid #eceef1",
|
||||
background: "#f3f4f4",
|
||||
|
@ -1854,6 +1854,8 @@ const Dark = {
|
||||
|
||||
headerBurgerColor: "#606060",
|
||||
|
||||
verticalLine: "1px solid #474747",
|
||||
|
||||
profile: {
|
||||
borderTop: "1px solid #474747",
|
||||
background: "#3D3D3D",
|
||||
|
@ -3138,7 +3138,7 @@ public class FileStorageService<T> //: IFileStorageService
|
||||
continue;
|
||||
}
|
||||
|
||||
var link = _roomLinkService.GetInvitationLink(user.Email, _authContext.CurrentAccount.ID);
|
||||
var link = _roomLinkService.GetInvitationLink(user.Email, share.Access, _authContext.CurrentAccount.ID);
|
||||
_studioNotifyService.SendEmailRoomInvite(user.Email, link);
|
||||
}
|
||||
}
|
||||
|
@ -47,24 +47,33 @@ public class RoomLinkService
|
||||
return _commonLinkUtility.GetConfirmationUrl(key, ConfirmType.LinkInvite, createdBy);
|
||||
}
|
||||
|
||||
public string GetInvitationLink(string email, Guid createdBy)
|
||||
public string GetInvitationLink(string email, FileShare share, Guid createdBy)
|
||||
{
|
||||
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, EmployeeType.RoomAdmin, createdBy)
|
||||
+ $"&emplType={EmployeeType.RoomAdmin:d}";
|
||||
var type = DocSpaceHelper.PaidRights.Contains(share) ? EmployeeType.RoomAdmin : EmployeeType.User;
|
||||
|
||||
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, type, createdBy)
|
||||
+ $"&emplType={type:d}";
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
public string GetInvitationLink(string email, EmployeeType employeeType, Guid createdBy)
|
||||
{
|
||||
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, employeeType, createdBy)
|
||||
+ $"&emplType={employeeType:d}";
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
public async Task<LinkOptions> GetOptionsAsync(string key, string email)
|
||||
{
|
||||
var options = new LinkOptions();
|
||||
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
options.Type = LinkType.DefaultInvintation;
|
||||
options.IsCorrect = true;
|
||||
return await GetOptionsAsync(key, email, EmployeeType.All);
|
||||
}
|
||||
|
||||
public async Task<LinkOptions> GetOptionsAsync(string key, string email, EmployeeType employeeType)
|
||||
{
|
||||
var options = new LinkOptions();
|
||||
|
||||
var payload = _docSpaceLinksHelper.Parse(key);
|
||||
|
||||
if (payload != default)
|
||||
@ -74,16 +83,24 @@ public class RoomLinkService
|
||||
if (record != null)
|
||||
{
|
||||
options.IsCorrect = true;
|
||||
options.Type = LinkType.InvintationToRoom;
|
||||
options.LinkType = LinkType.InvintationToRoom;
|
||||
options.RoomId = record.EntryId.ToString();
|
||||
options.Share = record.Share;
|
||||
options.Id = record.Subject;
|
||||
options.EmployeeType = DocSpaceHelper.PaidRights.Contains(record.Share) ? EmployeeType.RoomAdmin : EmployeeType.User;
|
||||
}
|
||||
}
|
||||
else if (_docSpaceLinksHelper.Validate(key, email) == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
else if (_docSpaceLinksHelper.ValidateEmailLink(email, key, employeeType) == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
{
|
||||
options.IsCorrect = true;
|
||||
options.Type = LinkType.InvintationByEmail;
|
||||
options.LinkType = LinkType.InvintationByEmail;
|
||||
options.EmployeeType = employeeType;
|
||||
}
|
||||
else if (_docSpaceLinksHelper.ValidateExtarnalLink(key, employeeType) == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
{
|
||||
options.LinkType = LinkType.DefaultInvintation;
|
||||
options.IsCorrect = true;
|
||||
options.EmployeeType = employeeType;
|
||||
}
|
||||
|
||||
return options;
|
||||
@ -105,7 +122,8 @@ public class LinkOptions
|
||||
public Guid Id { get; set; }
|
||||
public string RoomId { get; set; }
|
||||
public FileShare Share { get; set; }
|
||||
public LinkType Type { get; set; }
|
||||
public LinkType LinkType { get; set; }
|
||||
public EmployeeType EmployeeType { get; set; }
|
||||
public bool IsCorrect { get; set; }
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@ namespace ASC.Files.Core.Helpers;
|
||||
|
||||
public static class DocSpaceHelper
|
||||
{
|
||||
public static HashSet<FileShare> PaidRights { get; } = new HashSet<FileShare> { FileShare.RoomAdmin };
|
||||
|
||||
private static readonly HashSet<FileShare> _fillingFormRoomConstraints
|
||||
= new HashSet<FileShare> { FileShare.RoomAdmin, FileShare.FillForms, FileShare.Read };
|
||||
private static readonly HashSet<FileShare> _collaborationRoomConstraints
|
||||
|
@ -40,11 +40,11 @@ public class FileSharingAceHelper<T>
|
||||
private readonly GlobalFolderHelper _globalFolderHelper;
|
||||
private readonly FileSharingHelper _fileSharingHelper;
|
||||
private readonly FileTrackerHelper _fileTracker;
|
||||
private readonly FileSecurityCommon _fileSecurityCommon;
|
||||
private readonly FilesSettingsHelper _filesSettingsHelper;
|
||||
private readonly RoomLinkService _roomLinkService;
|
||||
private readonly StudioNotifyService _studioNotifyService;
|
||||
private readonly UsersInRoomChecker _usersInRoomChecker;
|
||||
private readonly UserManagerWrapper _userManagerWrapper;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public FileSharingAceHelper(
|
||||
@ -59,12 +59,12 @@ public class FileSharingAceHelper<T>
|
||||
GlobalFolderHelper globalFolderHelper,
|
||||
FileSharingHelper fileSharingHelper,
|
||||
FileTrackerHelper fileTracker,
|
||||
FileSecurityCommon fileSecurityCommon,
|
||||
FilesSettingsHelper filesSettingsHelper,
|
||||
RoomLinkService roomLinkService,
|
||||
StudioNotifyService studioNotifyService,
|
||||
ILoggerProvider loggerProvider,
|
||||
UsersInRoomChecker usersInRoomChecker)
|
||||
UsersInRoomChecker usersInRoomChecker,
|
||||
UserManagerWrapper userManagerWrapper)
|
||||
{
|
||||
_fileSecurity = fileSecurity;
|
||||
_coreBaseSettings = coreBaseSettings;
|
||||
@ -78,11 +78,11 @@ public class FileSharingAceHelper<T>
|
||||
_fileSharingHelper = fileSharingHelper;
|
||||
_fileTracker = fileTracker;
|
||||
_filesSettingsHelper = filesSettingsHelper;
|
||||
_fileSecurityCommon = fileSecurityCommon;
|
||||
_roomLinkService = roomLinkService;
|
||||
_studioNotifyService = studioNotifyService;
|
||||
_usersInRoomChecker = usersInRoomChecker;
|
||||
_logger = loggerProvider.CreateLogger("ASC.Files");
|
||||
_userManagerWrapper = userManagerWrapper;
|
||||
}
|
||||
|
||||
public async Task<bool> SetAceObjectAsync(List<AceWrapper> aceWrappers, FileEntry<T> entry, bool notify, string message, AceAdvancedSettingsWrapper advancedSettings)
|
||||
@ -117,7 +117,7 @@ public class FileSharingAceHelper<T>
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ProcessEmailAce(w))
|
||||
if (!await ProcessEmailAceAsync(w))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -159,7 +159,7 @@ public class FileSharingAceHelper<T>
|
||||
|
||||
if (!string.IsNullOrEmpty(w.Email))
|
||||
{
|
||||
var link = _roomLinkService.GetInvitationLink(w.Email, _authContext.CurrentAccount.ID);
|
||||
var link = _roomLinkService.GetInvitationLink(w.Email, share, _authContext.CurrentAccount.ID);
|
||||
_studioNotifyService.SendEmailRoomInvite(w.Email, link);
|
||||
_logger.Debug(link);
|
||||
}
|
||||
@ -271,30 +271,25 @@ public class FileSharingAceHelper<T>
|
||||
await _fileMarker.RemoveMarkAsNewAsync(entry);
|
||||
}
|
||||
|
||||
private bool ProcessEmailAce(AceWrapper ace)
|
||||
private async Task<bool> ProcessEmailAceAsync(AceWrapper ace)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ace.Email))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!MailAddress.TryCreate(ace.Email, out var email) || _userManager.GetUserByEmail(ace.Email) != Constants.LostUser)
|
||||
var type = DocSpaceHelper.PaidRights.Contains(ace.Access) ? EmployeeType.RoomAdmin : EmployeeType.User;
|
||||
UserInfo user = null;
|
||||
|
||||
try
|
||||
{
|
||||
user = await _userManagerWrapper.AddInvitedUserAsync(ace.Email, type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var userInfo = new UserInfo
|
||||
{
|
||||
Email = email.Address,
|
||||
UserName = email.User,
|
||||
LastName = string.Empty,
|
||||
FirstName = string.Empty,
|
||||
ActivationStatus = EmployeeActivationStatus.Pending,
|
||||
Status = EmployeeStatus.Active
|
||||
};
|
||||
|
||||
var user = _userManager.SaveUserInfo(userInfo);
|
||||
|
||||
ace.Id = user.Id;
|
||||
|
||||
return true;
|
||||
|
@ -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 ASC.Common.Log;
|
||||
|
||||
namespace ASC.People.Api;
|
||||
|
||||
public class UserController : PeopleControllerBase
|
||||
@ -203,16 +205,18 @@ public class UserController : PeopleControllerBase
|
||||
|
||||
_permissionContext.DemandPermissions(Constants.Action_AddRemoveUser);
|
||||
|
||||
var options = inDto.FromInviteLink ? await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email) : null;
|
||||
var options = inDto.FromInviteLink ? await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email, inDto.Type) : null;
|
||||
|
||||
if (options != null && !options.IsCorrect)
|
||||
{
|
||||
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
|
||||
}
|
||||
|
||||
inDto.Type = options != null ? options.EmployeeType : inDto.Type;
|
||||
|
||||
var user = new UserInfo();
|
||||
|
||||
var byEmail = options != null && options.Type == LinkType.InvintationByEmail;
|
||||
var byEmail = options?.LinkType == LinkType.InvintationByEmail;
|
||||
|
||||
if (byEmail)
|
||||
{
|
||||
@ -259,7 +263,7 @@ public class UserController : PeopleControllerBase
|
||||
|
||||
UpdateContacts(inDto.Contacts, user);
|
||||
_cache.Insert("REWRITE_URL" + _tenantManager.GetCurrentTenant().Id, HttpContext.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5));
|
||||
user = _userManagerWrapper.AddUser(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.IsUser, inDto.FromInviteLink, true, true, byEmail);
|
||||
user = _userManagerWrapper.AddUser(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.Type == EmployeeType.User, inDto.FromInviteLink, true, true, byEmail, inDto.Type == EmployeeType.DocSpaceAdmin);
|
||||
|
||||
UpdateDepartments(inDto.Department, user);
|
||||
|
||||
@ -268,19 +272,19 @@ public class UserController : PeopleControllerBase
|
||||
await UpdatePhotoUrl(inDto.Files, user);
|
||||
}
|
||||
|
||||
if (options != null && options.Type == LinkType.InvintationToRoom)
|
||||
if (options != null && options.LinkType == LinkType.InvintationToRoom)
|
||||
{
|
||||
var success = int.TryParse(options.RoomId, out var id);
|
||||
|
||||
if (success)
|
||||
{
|
||||
await _usersInRoomChecker.CheckAppend();
|
||||
await _fileSecurity.ShareAsync(id, Files.Core.FileEntryType.Folder, user.Id, options.Share);
|
||||
await _fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, options.Share);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _usersInRoomChecker.CheckAppend();
|
||||
await _fileSecurity.ShareAsync(options.RoomId, Files.Core.FileEntryType.Folder, user.Id, options.Share);
|
||||
await _fileSecurity.ShareAsync(options.RoomId, FileEntryType.Folder, user.Id, options.Share);
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,6 +294,26 @@ public class UserController : PeopleControllerBase
|
||||
return await _employeeFullDtoHelper.GetFull(user);
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public async IAsyncEnumerable<EmployeeDto> InviteUsersAsync(InviteUsersRequestDto inDto)
|
||||
{
|
||||
foreach (var invite in inDto.Invitations)
|
||||
{
|
||||
var user = await _userManagerWrapper.AddInvitedUserAsync(invite.Email, invite.Type);
|
||||
var link = _roomLinkService.GetInvitationLink(user.Email, invite.Type, _authContext.CurrentAccount.ID);
|
||||
|
||||
_studioNotifyService.SendDocSpaceInvite(user.Email, link);
|
||||
_logger.Debug(link);
|
||||
}
|
||||
|
||||
var users = _userManager.GetUsers().Where(u => u.ActivationStatus == EmployeeActivationStatus.Pending);
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
yield return await _employeeDtoHelper.Get(user);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{userid}/password")]
|
||||
[Authorize(AuthenticationSchemes = "confirm", Roles = "PasswordChange,EmailChange,Activation,EmailActivation,Everyone")]
|
||||
public async Task<EmployeeFullDto> ChangeUserPassword(Guid userid, MemberRequestDto inDto)
|
||||
@ -672,14 +696,10 @@ public class UserController : PeopleControllerBase
|
||||
|
||||
if (user.ActivationStatus == EmployeeActivationStatus.Pending)
|
||||
{
|
||||
if (_userManager.IsUser(user))
|
||||
{
|
||||
_studioNotifyService.GuestInfoActivation(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_studioNotifyService.UserInfoActivation(user);
|
||||
}
|
||||
var type = _userManager.IsDocSpaceAdmin(user) ? EmployeeType.DocSpaceAdmin :
|
||||
_userManager.IsUser(user) ? EmployeeType.User : EmployeeType.RoomAdmin;
|
||||
|
||||
_studioNotifyService.SendDocSpaceInvite(user.Email, _roomLinkService.GetInvitationLink(user.Email, type, _authContext.CurrentAccount.ID));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -0,0 +1,38 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.People.ApiModels.RequestDto;
|
||||
|
||||
public class InviteUsersRequestDto
|
||||
{
|
||||
public IEnumerable<UserInvitation> Invitations { get; set; }
|
||||
}
|
||||
|
||||
public class UserInvitation
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public EmployeeType Type { get; set; }
|
||||
}
|
@ -28,6 +28,7 @@ namespace ASC.People.ApiModels.RequestDto;
|
||||
|
||||
public class MemberRequestDto
|
||||
{
|
||||
public EmployeeType Type { get; set; }
|
||||
public bool IsUser { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Firstname { get; set; }
|
||||
|
@ -163,5 +163,6 @@ public static class Actions
|
||||
public static readonly INotifyAction StorageDecryptionSuccess = new NotifyAction("storage_decryption_success");
|
||||
public static readonly INotifyAction StorageDecryptionError = new NotifyAction("storage_decryption_error");
|
||||
|
||||
public static readonly INotifyAction RoomInvite = new NotifyAction("room_invite");
|
||||
public static readonly INotifyAction RoomInvite = new NotifyAction("room_invite", "room_invite");
|
||||
public static readonly INotifyAction DocSpaceInvite = new NotifyAction("docspace_invite", "docspace_invite");
|
||||
}
|
||||
|
@ -237,6 +237,18 @@ public class StudioNotifyService
|
||||
TagValues.GreenButton(greenButtonText, confirmationUrl));
|
||||
}
|
||||
|
||||
public void SendDocSpaceInvite(string email, string confirmationUrl)
|
||||
{
|
||||
static string greenButtonText() => WebstudioNotifyPatternResource.ButtonConfirmDocSpaceInvite;
|
||||
|
||||
_client.SendNoticeToAsync(
|
||||
Actions.DocSpaceInvite,
|
||||
_studioNotifyHelper.RecipientFromEmail(email, false),
|
||||
new[] { EMailSenderName },
|
||||
new TagValue(Tags.InviteLink, confirmationUrl),
|
||||
TagValues.GreenButton(greenButtonText, confirmationUrl));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MailServer
|
||||
|
@ -303,6 +303,15 @@ namespace ASC.Web.Core.PublicResources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Confirm DocSpace Invite.
|
||||
/// </summary>
|
||||
public static string ButtonConfirmDocSpaceInvite {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonConfirmDocSpaceInvite", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Confirm Portal Address Change.
|
||||
/// </summary>
|
||||
|
@ -2121,4 +2121,7 @@ $Body</value>
|
||||
<data name="subject_for_sales_notify" xml:space="preserve">
|
||||
<value>Sales department request</value>
|
||||
</data>
|
||||
<data name="ButtonConfirmDocSpaceInvite" xml:space="preserve">
|
||||
<value>Confirm DocSpace Invite</value>
|
||||
</data>
|
||||
</root>
|
@ -48,6 +48,7 @@ public sealed class UserManagerWrapper
|
||||
private readonly DisplayUserSettingsHelper _displayUserSettingsHelper;
|
||||
private readonly SettingsManager _settingsManager;
|
||||
private readonly UserFormatter _userFormatter;
|
||||
private readonly CountRoomAdminChecker _countManagerChecker;
|
||||
|
||||
public UserManagerWrapper(
|
||||
StudioNotifyService studioNotifyService,
|
||||
@ -60,7 +61,8 @@ public sealed class UserManagerWrapper
|
||||
IPSecurity.IPSecurity iPSecurity,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
SettingsManager settingsManager,
|
||||
UserFormatter userFormatter)
|
||||
UserFormatter userFormatter,
|
||||
CountRoomAdminChecker countManagerChecker)
|
||||
{
|
||||
_studioNotifyService = studioNotifyService;
|
||||
_userManager = userManager;
|
||||
@ -73,6 +75,7 @@ public sealed class UserManagerWrapper
|
||||
_displayUserSettingsHelper = displayUserSettingsHelper;
|
||||
_settingsManager = settingsManager;
|
||||
_userFormatter = userFormatter;
|
||||
_countManagerChecker = countManagerChecker;
|
||||
}
|
||||
|
||||
private bool TestUniqueUserName(string uniqueName)
|
||||
@ -108,8 +111,49 @@ public sealed class UserManagerWrapper
|
||||
return Equals(foundUser, Constants.LostUser) || foundUser.Id == userId;
|
||||
}
|
||||
|
||||
public async Task<UserInfo> AddInvitedUserAsync(string email, EmployeeType type)
|
||||
{
|
||||
var mail = new MailAddress(email);
|
||||
|
||||
if (_userManager.GetUserByEmail(mail.Address).Id != Constants.LostUser.Id)
|
||||
{
|
||||
throw new InvalidOperationException($"User with email {mail.Address} already exists or is invited");
|
||||
}
|
||||
|
||||
if (type is EmployeeType.RoomAdmin or EmployeeType.DocSpaceAdmin)
|
||||
{
|
||||
await _countManagerChecker.CheckAppend();
|
||||
}
|
||||
|
||||
var user = new UserInfo
|
||||
{
|
||||
Email = mail.Address,
|
||||
UserName = mail.User,
|
||||
LastName = string.Empty,
|
||||
FirstName = string.Empty,
|
||||
ActivationStatus = EmployeeActivationStatus.Pending,
|
||||
Status = EmployeeStatus.Active,
|
||||
};
|
||||
|
||||
var newUser = _userManager.SaveUserInfo(user);
|
||||
|
||||
var groupId = type switch
|
||||
{
|
||||
EmployeeType.User => Constants.GroupUser.ID,
|
||||
EmployeeType.DocSpaceAdmin => Constants.GroupAdmin.ID,
|
||||
_ => Guid.Empty,
|
||||
};
|
||||
|
||||
if (groupId != Guid.Empty)
|
||||
{
|
||||
_userManager.AddUserIntoGroup(newUser.Id, groupId);
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
public UserInfo AddUser(UserInfo userInfo, string passwordHash, bool afterInvite = false, bool notify = true, bool isUser = false, bool fromInviteLink = false, bool makeUniqueName = true, bool isCardDav = false,
|
||||
bool updateExising = false)
|
||||
bool updateExising = false, bool isAdmin = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(userInfo);
|
||||
|
||||
@ -184,6 +228,10 @@ public sealed class UserManagerWrapper
|
||||
{
|
||||
_userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupUser.ID);
|
||||
}
|
||||
else if (isAdmin)
|
||||
{
|
||||
_userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupAdmin.ID);
|
||||
}
|
||||
|
||||
return newUserInfo;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user