Merge branch 'develop' into feature/socket-newtag

This commit is contained in:
Alexey Safronov 2022-11-30 18:37:34 +03:00
commit fbfd9daa46
89 changed files with 1134 additions and 523 deletions

View File

@ -2,7 +2,7 @@ name: 4testing multiarch-build
on:
push:
branches: [ "develop" ]
branches: [ "release/rc-v1.2.0" ]
jobs:
build:
@ -33,10 +33,10 @@ jobs:
cd ./build/install/docker
REPO="onlyoffice" \
DOCKER_IMAGE_PREFIX="4testing-docspace" \
DOCKER_TAG="develop" \
DOCKER_TAG="rc-v1.2.0" \
DOCKERFILE="Dockerfile.app" \
docker buildx bake -f build.yml \
--set *.args.GIT_BRANCH="develop" \
--set *.args.GIT_BRANCH="release/rc-v1.2.0" \
--set *.platform=linux/amd64 \
--push
shell: bash

View File

@ -0,0 +1,7 @@
@echo off
pwsh %~dp0/build.document.server.docker.ps1 %1
echo.
pause

View File

@ -0,0 +1,15 @@
$PSversionMajor = $PSVersionTable.PSVersion | sort-object major | ForEach-Object { $_.major }
$PSversionMinor = $PSVersionTable.PSVersion | sort-object minor | ForEach-Object { $_.minor }
if ($PSversionMajor -lt 7 -or $PSversionMinor -lt 2) {
Write-Error "Powershell version must be greater than or equal to 7.2."
exit
}
$RootDir = Split-Path -Parent $PSScriptRoot
Write-Host "Run Document server" -ForegroundColor Green
$DOCUMENT_SERVER_IMAGE_NAME = "onlyoffice/documentserver-de:latest"
docker run -i -t -d -p 8085:80 -e JWT_ENABLED=false -e JWT_IN_BODY=false --restart=always -v $RootDir/Data:/var/www/onlyoffice/Data $DOCUMENT_SERVER_IMAGE_NAME

View File

@ -161,8 +161,9 @@ public class SecurityContext
return false;
}
var settingLoginEvents = await _dbLoginEventsManager.GetLoginEventIds(tenant, userid);
if (loginEventId != 0 && !settingLoginEvents.Contains(loginEventId))
var loginEventById = await _dbLoginEventsManager.GetById(loginEventId);
if (loginEventById == null)
{
return false;
}

View File

@ -0,0 +1,32 @@
// (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.Core;
public enum Payments
{
Paid = 0,
Free = 1
}

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 System.ComponentModel;
namespace ASC.Core.Data;
[Scope]
@ -62,25 +64,21 @@ public class DbLoginEventsManager
_mapper = mapper;
}
public async Task<List<int>> GetLoginEventIds(int tenantId, Guid userId)
public async Task<LoginEvent> GetById(int id)
{
var date = DateTime.UtcNow.AddYears(-1);
if (id < 0) return null;
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var resultList = await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && _loginActions.Contains(r.Action ?? 0) && r.Date >= date && r.Active)
.Select(r => r.Id)
.ToListAsync();
return resultList;
return await loginEventContext.LoginEvents.FindAsync(id);
}
public async Task<List<BaseEvent>> GetLoginEvents(int tenantId, Guid userId)
{
var date = DateTime.UtcNow.AddYears(-1);
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var loginInfo = await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && _loginActions.Contains(r.Action ?? 0) && r.Date >= date && r.Active)
.OrderByDescending(r => r.Id)
@ -91,7 +89,8 @@ public class DbLoginEventsManager
public async Task LogOutEvent(int loginEventId)
{
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents
.Where(r => r.Id == loginEventId)
.ToListAsync();
@ -108,7 +107,8 @@ public class DbLoginEventsManager
public async Task LogOutAllActiveConnections(int tenantId, Guid userId)
{
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Active)
.ToListAsync();
@ -125,7 +125,8 @@ public class DbLoginEventsManager
public async Task LogOutAllActiveConnectionsForTenant(int tenantId)
{
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.Active)
.ToListAsync();
@ -140,7 +141,8 @@ public class DbLoginEventsManager
public async Task LogOutAllActiveConnectionsExceptThis(int loginEventId, int tenantId, Guid userId)
{
using var loginEventContext = _dbContextFactory.CreateDbContext();
using var loginEventContext = await _dbContextFactory.CreateDbContextAsync();
var events = await loginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Id != loginEventId && r.Active)
.ToListAsync();

View File

@ -220,9 +220,20 @@ public class EFUserService : IUserService
q = GetUserQueryForFilter(userDbContext, q, isDocSpaceAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text);
if (!string.IsNullOrEmpty(sortBy))
{
if (sortBy == "type")
{
var q1 = from user in q join userGroup in userDbContext.UserGroups.Where(g => !g.Removed && (g.UserGroupId == Users.Constants.GroupAdmin.ID || g.UserGroupId == Users.Constants.GroupUser.ID))
on user.Id equals userGroup.Userid into joinedGroup from @group in joinedGroup.DefaultIfEmpty() select new { user, @group };
q = sortOrderAsc ? q1.OrderBy(r => r.group != null && r.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 : r.group == null ? 2 : 3).Select(r => r.user)
: q1.OrderByDescending(u => u.group != null && u.group.UserGroupId == Users.Constants.GroupAdmin.ID ? 1 : u.group == null ? 2 : 3).Select(r => r.user);
}
else
{
q = q.OrderBy(sortBy, sortOrderAsc);
}
}
if (offset != 0)
{

View File

@ -19,7 +19,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MySql.Data" Version="8.0.30" />
<PackageReference Include="MySql.Data" Version="8.0.31" />
<PackageReference Include="protobuf-net" Version="3.1.17" />
</ItemGroup>
<ItemGroup>

View File

@ -125,7 +125,7 @@
"ConnectionStrings": {
"default": {
"name": "default",
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
"providerName": "MySql.Data.MySqlClient"
},
"postgre": {
@ -135,7 +135,7 @@
},
"mysql": {
"name": "mysql",
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;Connection Timeout=30;Maximum Pool Size=300;ConnectionReset=false",
"providerName": "MySql.Data.MySqlClient"
}
},

View File

@ -89,6 +89,14 @@ const ArchiveDialogComponent = (props) => {
const acceptButton =
action === "archive" ? t("Common:OKButton") : t("Common:Restore");
const isArchive = action === "archive";
const idButtonSubmit = isArchive
? "shared_move-to-archived-modal_submit"
: "restore-all_submit";
const idButtonCancel = isArchive
? "shared_move-to-archived-modal_cancel"
: "restore-all_cancel";
return (
<StyledModal
isLoading={!tReady}
@ -102,6 +110,7 @@ const ArchiveDialogComponent = (props) => {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id={idButtonSubmit}
key="OkButton"
label={acceptButton}
size="normal"
@ -111,6 +120,7 @@ const ArchiveDialogComponent = (props) => {
scale
/>
<Button
id={idButtonCancel}
key="CancelButton"
label={t("Common:CancelButton")}
size="normal"

View File

@ -98,6 +98,7 @@ class ChangeUserStatusDialogComponent extends React.Component {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id="change-user-status-modal_submit"
label={t("ChangeUsersStatusButton")}
size="normal"
primary
@ -107,6 +108,7 @@ class ChangeUserStatusDialogComponent extends React.Component {
isDisabled={userIDs.length === 0}
/>
<Button
id="change-user-status-modal_cancel"
label={t("Common:CancelButton")}
size="normal"
scale

View File

@ -109,6 +109,7 @@ class ChangeUserTypeDialogComponent extends React.Component {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id="change-user-type-modal_submit"
label={t("ChangeUserTypeButton")}
size="normal"
scale
@ -118,6 +119,7 @@ class ChangeUserTypeDialogComponent extends React.Component {
isDisabled={!userIDs.length}
/>
<Button
id="change-user-type-modal_cancel"
label={t("Common:CancelButton")}
size="normal"
scale

View File

@ -149,6 +149,7 @@ const CreateRoomDialog = ({
{!!roomParams.type && (
<ModalDialog.Footer>
<Button
id="shared_create-room-modal_submit"
tabIndex={5}
label={t("Common:Create")}
size="normal"
@ -158,6 +159,7 @@ const CreateRoomDialog = ({
isLoading={isLoading}
/>
<Button
id="shared_create-room-modal_cancel"
tabIndex={5}
label={t("Common:CancelButton")}
size="normal"

View File

@ -2,6 +2,7 @@ import { RoomsType } from "@docspace/common/constants";
export const roomTypes = [
{
id: "shared_filling-forms-room",
type: RoomsType.FillingFormsRoom,
title: "FillingFormsRoomTitle",
description: "FillingFormsRoomDescription",
@ -10,6 +11,7 @@ export const roomTypes = [
secondaryInfo: "FillingFormsRoomSecondaryInfo",
},
{
id: "shared_collaboration-room",
type: RoomsType.EditingRoom,
title: "CollaborationRoomTitle",
description: "CollaborationRoomDescription",
@ -18,6 +20,7 @@ export const roomTypes = [
secondaryInfo: "CollaborationRoomSecondaryInfo",
},
{
id: "shared_review-room",
type: RoomsType.ReviewRoom,
title: "ReviewRoomTitle",
description: "ReviewRoomDescription",
@ -26,6 +29,7 @@ export const roomTypes = [
secondaryInfo: "ReviewRoomSecondaryInfo",
},
{
id: "shared_read-only-room",
type: RoomsType.ReadOnlyRoom,
title: "ViewOnlyRoomTitle",
description: "ViewOnlyRoomDescription",
@ -34,6 +38,7 @@ export const roomTypes = [
secondaryInfo: "ViewOnlyRoomSecondaryInfo",
},
{
id: "shared_custom-room",
type: RoomsType.CustomRoom,
title: "CustomRoomTitle",
description: "CustomRoomDescription",

View File

@ -21,7 +21,13 @@ const StyledToggleParam = styled(StyledParam)`
}
`;
const ToggleParam = ({ title, description, isChecked, onCheckedChange }) => {
const ToggleParam = ({
id,
title,
description,
isChecked,
onCheckedChange,
}) => {
return (
<StyledToggleParam isPrivate>
<div className="set_room_params-info">
@ -31,6 +37,7 @@ const ToggleParam = ({ title, description, isChecked, onCheckedChange }) => {
<div className="set_room_params-info-description">{description}</div>
</div>
<ToggleButton
id={id}
className="set_room_params-toggle"
isChecked={isChecked}
onChange={onCheckedChange}

View File

@ -131,7 +131,15 @@ const StyledDisplayItem = styled(StyledRoomType)`
}
`;
const RoomType = ({ t, room, onClick, type = "listItem", isOpen }) => {
const RoomType = ({
t,
room,
onClick,
type = "listItem",
isOpen,
id,
selectedId,
}) => {
const arrowClassName =
type === "dropdownButton"
? "choose_room-forward_btn dropdown-button"
@ -170,23 +178,37 @@ const RoomType = ({ t, room, onClick, type = "listItem", isOpen }) => {
);
return type === "listItem" ? (
<StyledListItem title={t(room.title)} onClick={onClick}>
<StyledListItem id={id} title={t(room.title)} onClick={onClick}>
{content}
</StyledListItem>
) : type === "dropdownButton" ? (
<StyledDropdownButton
id={id}
title={t(room.title)}
onClick={onClick}
isOpen={isOpen}
data-selected-id={selectedId}
>
{content}
</StyledDropdownButton>
) : type === "dropdownItem" ? (
<StyledDropdownItem title={t(room.title)} onClick={onClick} isOpen={isOpen}>
<StyledDropdownItem
id={id}
title={t(room.title)}
onClick={onClick}
isOpen={isOpen}
data-selected-id={selectedId}
>
{content}
</StyledDropdownItem>
) : (
<StyledDisplayItem title={t(room.title)}>{content}</StyledDisplayItem>
<StyledDisplayItem
id={id}
title={t(room.title)}
data-selected-id={selectedId}
>
{content}
</StyledDisplayItem>
);
};

View File

@ -41,6 +41,7 @@ const DropdownDesktop = ({ t, open, roomTypes, chooseRoomType }) => {
<div className="dropdown-content">
{roomTypes.map((room) => (
<RoomType
id={room.id}
t={t}
key={room.type}
room={room}

View File

@ -72,6 +72,7 @@ const DropdownMobile = ({ t, open, onClose, roomTypes, chooseRoomType }) => {
<div className="dropdown-mobile-content">
{roomTypes.map((room) => (
<RoomType
id={room.id}
t={t}
key={room.type}
room={room}

View File

@ -37,11 +37,12 @@ const RoomTypeDropdown = ({
setRoomType(roomType);
toggleDropdown();
};
return (
<StyledRoomTypeDropdown isOpen={isOpen}>
<RoomType
t={t}
id="shared_select-room"
selectedId={currentRoom.id}
room={currentRoom}
type="dropdownButton"
isOpen={isOpen}

View File

@ -21,6 +21,7 @@ const RoomTypeList = ({ t, setRoomType }) => {
<StyledRoomTypeList>
{roomTypes.map((room) => (
<RoomType
id={room.id}
t={t}
key={room.type}
room={room}

View File

@ -74,7 +74,7 @@ const SetRoomParams = ({
)}
<InputParam
id={"room-name"}
id="shared_room-name"
title={`${t("Common:Name")}:`}
placeholder={t("NamePlaceholder")}
value={roomParams.title}

View File

@ -19,7 +19,9 @@ const TagDropdown = ({
const [dropdownMaxHeight, setDropdownMaxHeight] = useState(0);
useEffect(() => {
document.getElementById("tags-input").addEventListener("keyup", onKeyPress);
document
.getElementById("shared_tags-input")
.addEventListener("keyup", onKeyPress);
return () => document.removeEventListener("keyup", onKeyPress);
});
@ -39,7 +41,7 @@ const TagDropdown = ({
};
const onClickOutside = () => {
document.getElementById("tags-input").blur();
document.getElementById("shared_tags-input").blur();
};
const addNewTag = () => {

View File

@ -50,7 +50,7 @@ const TagInput = ({
return (
<StyledTagInput className="set_room_params-input set_room_params-tag_input">
<InputParam
id={"tags-input"}
id="shared_tags-input"
title={`${t("Common:Tags")}:`}
placeholder={t("TagsPlaceholder")}
value={tagInput}

View File

@ -221,6 +221,7 @@ const ThirdPartyComboBox = ({
<StyledStorageLocation isOpen={isOpen}>
<div className="set_room_params-thirdparty">
<div
id="shared_third-party-storage_combobox"
className="set_room_params-thirdparty-combobox"
onClick={toggleIsOpen}
>
@ -235,6 +236,7 @@ const ThirdPartyComboBox = ({
</div>
<Button
id="shared_third-party-storage_connect"
isDisabled={
!storageLocation?.provider ||
!!storageLocation?.thirdpartyAccount ||
@ -262,7 +264,8 @@ const ThirdPartyComboBox = ({
>
{thirdparties.map((thirdparty) => (
<DropDownItem
className="dropdown-item"
id={thirdparty.id}
className={`dropdown-item ${thirdparty.className ?? ""}`}
label={thirdparty.title}
key={thirdparty.id}
height={32}

View File

@ -90,6 +90,7 @@ const ThirdPartyStorage = ({
return (
<StyledThirdPartyStorage>
<ToggleParam
id="shared_third-party-storage-toggle"
title={t("ThirdPartyStorageTitle")}
description={t("ThirdPartyStorageDescription")}
isChecked={storageLocation.isThirdparty}
@ -188,6 +189,7 @@ export default inject(
(item) =>
item && {
id: item[0],
className: `storage_${item[0].toLowerCase()}`,
providerKey: item[0],
isOauth: item.length > 1 && item[0] !== "WebDav",
oauthHref: item.length > 1 && item[0] !== "WebDav" ? item[1] : "",

View File

@ -159,6 +159,7 @@ const DeleteDialogComponent = (props) => {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id="delete-file-modal_submit"
key="OkButton"
label={accessButtonLabel}
size="normal"
@ -171,6 +172,7 @@ const DeleteDialogComponent = (props) => {
isDisabled={!selection.length}
/>
<Button
id="delete-file-modal_cancel"
key="CancelButton"
label={t("Common:CancelButton")}
size="normal"

View File

@ -77,6 +77,7 @@ const EmptyTrashDialogComponent = (props) => {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id="empty-archive_delete-submit"
key="OkButton"
label={t("DeleteForeverButton")}
size="normal"
@ -86,6 +87,7 @@ const EmptyTrashDialogComponent = (props) => {
scale
/>
<Button
id="empty-archive_delete-cancel"
key="CancelButton"
label={t("Common:CancelButton")}
size="normal"

View File

@ -129,6 +129,7 @@ class SendInviteDialogComponent extends React.Component {
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
id="send-inite-again-modal_submit"
label={t("Common:SendButton")}
size="normal"
scale
@ -138,6 +139,7 @@ class SendInviteDialogComponent extends React.Component {
isDisabled={!userIds.length}
/>
<Button
id="send-inite-again-modal_cancel"
label={t("Common:CancelButton")}
size="normal"
scale

View File

@ -134,6 +134,7 @@ const SelectionPanelBody = ({
<div>
<Button
id="select-file-modal-submit"
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
@ -153,6 +154,7 @@ const SelectionPanelBody = ({
isLoading={isDisableTree}
/>
<Button
id="select-file-modal-cancel"
theme={theme}
className="modal-dialog-button"
size="normal"

View File

@ -159,7 +159,7 @@ const SectionHeaderContent = (props) => {
getHeaderMenu,
cbMenuItems,
getCheckboxItemLabel,
getMenuItemId,
setInfoPanelIsVisible,
isInfoPanelVisible,
isOwner,
@ -186,8 +186,10 @@ const SectionHeaderContent = (props) => {
<>
{cbMenuItems.map((key) => {
const label = getCheckboxItemLabel(t, key);
const id = getMenuItemId(key);
return (
<DropDownItem
id={id}
key={key}
label={label}
data-key={key}
@ -226,7 +228,7 @@ const SectionHeaderContent = (props) => {
const getContextOptions = () => {
return [
isOwner && {
id: "main-button_administrator",
id: "accounts-add_administrator",
className: "main-button_drop-down",
icon: "/static/images/person.admin.react.svg",
label: t("Common:DocSpaceAdmin"),
@ -235,7 +237,7 @@ const SectionHeaderContent = (props) => {
key: "administrator",
},
{
id: "main-button_manager",
id: "accounts-add_manager",
className: "main-button_drop-down",
icon: "/static/images/person.manager.react.svg",
label: t("Common:RoomAdmin"),
@ -244,7 +246,7 @@ const SectionHeaderContent = (props) => {
key: "manager",
},
{
id: "main-button_user",
id: "accounts-add_user",
className: "main-button_drop-down",
icon: "/static/images/person.user.react.svg",
label: t("Common:User"),
@ -257,7 +259,7 @@ const SectionHeaderContent = (props) => {
isSeparator: true,
},
{
id: "main-button_invite-again",
id: "accounts-add_invite-again",
className: "main-button_drop-down",
icon: "/static/images/invite.again.react.svg",
label: t("LblInviteAgain"),
@ -296,6 +298,7 @@ const SectionHeaderContent = (props) => {
{t("Accounts")}
</Headline>
<ContextMenuButton
id="header_add-button"
className="action-button"
directionX="left"
title={t("Common:Actions")}
@ -314,6 +317,7 @@ const SectionHeaderContent = (props) => {
) && (
<div className="info-panel-toggle-bg">
<IconButton
id="info-panel-toggle--open"
className="info-panel-toggle"
iconName="images/panel.react.svg"
size="16"
@ -349,6 +353,7 @@ export default withRouter(
isHeaderIndeterminate,
isHeaderChecked,
cbMenuItems,
getMenuItemId,
getCheckboxItemLabel,
} = headerMenuStore;
@ -361,6 +366,7 @@ export default withRouter(
isHeaderChecked,
getHeaderMenu,
cbMenuItems,
getMenuItemId,
getCheckboxItemLabel,
setInfoPanelIsVisible,
isInfoPanelVisible,

View File

@ -73,11 +73,43 @@ class DetailsHelper {
getPropertyList = () => {
return this.getNeededProperties().map((propertyId) => ({
id: propertyId,
className: this.getPropertyClassName(propertyId),
title: this.getPropertyTitle(propertyId),
content: this.getPropertyContent(propertyId),
}));
};
getPropertyClassName = (propertyId) => {
switch (propertyId) {
case "Owner":
return "info_details_owner";
case "Location":
return "info_details_location";
case "Type":
return "info_details_type";
case "Storage Type":
return "info_details_storage-type";
case "File extension":
return "info_details_file-extension";
case "Content":
return "info_details_content";
case "Size":
return "info_details_size";
case "Date modified":
return "info_details_date_modified";
case "Last modified by":
return "info_details_last-modified-by";
case "Creation date":
return "info_details_creation-date";
case "Versions":
return "info_details_versions";
case "Comments":
return "info_details_comments";
case "Tags":
return "info_details_tags";
}
};
getNeededProperties = () => {
return (this.item.isRoom
? [

View File

@ -0,0 +1,37 @@
import moment from "moment";
import { LANGUAGE } from "@docspace/common/constants";
import { getCookie } from "@docspace/common/utils";
export const getRelativeDateDay = (t, date) => {
moment.locale(getCookie(LANGUAGE));
const given = moment(date);
const now = moment();
const weekAgo = moment().subtract(1, "week");
const halfYearAgo = moment().subtract(6, "month");
if (given.isAfter(weekAgo)) {
if (now.weekday() === given.weekday()) return t("Common:Today");
if (now.weekday() - 1 === given.weekday()) return t("Common:Yesterday");
const weekday = moment.weekdays(given.weekday());
return weekday.charAt(0).toUpperCase() + weekday.slice(1);
}
if (given.isBetween(halfYearAgo, weekAgo)) {
const shortDate = given.format("MMMM D");
return shortDate.charAt(0).toUpperCase() + shortDate.slice(1);
}
const longDate = given.format("MMMM D, YYYY");
return longDate.charAt(0).toUpperCase() + longDate.slice(1);
};
export const getDateTime = (date) => {
moment.locale(getCookie(LANGUAGE));
const given = moment(date);
return given.format("LT");
};

View File

@ -69,16 +69,13 @@ const InfoPanelBodyContent = ({
if (isAccounts) return viewHelper.AccountsView();
switch (isRooms ? roomsView : fileView) {
case "members": {
case "info_members":
return viewHelper.MembersView();
}
case "history": {
case "info_history":
return viewHelper.HistoryView();
}
case "details": {
case "info_details":
return viewHelper.DetailsView();
}
}
};
//////////////////////////////////////////////////////////

View File

@ -4,9 +4,9 @@ import { Base } from "@docspace/components/themes";
import { mobile } from "@docspace/components/utils/device";
const StyledInfoPanelBody = styled.div`
padding: 0 3px 0 20px;
padding: 80px 3px 0 20px;
@media ${mobile} {
padding: 0 8px 0 16px;
padding: 80px 8px 0 16px;
}
height: auto;
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
@ -26,13 +26,24 @@ const StyledInfoPanelBody = styled.div`
`;
const StyledTitle = styled.div`
position: fixed;
margin-top: -80px;
margin-left: -20px;
width: calc(100% - 40px);
padding: 24px 0 24px 20px;
background: ${(props) => props.theme.infoPanel.backgroundColor};
z-index: 100;
@media ${mobile} {
width: calc(100% - 32px);
padding: 24px 0 24px 16px;
}
display: flex;
flex-wrap: no-wrap;
flex-direction: row;
align-items: center;
width: 100%;
height: 32px;
padding: 24px 0;
${(props) =>
props.withBottomBorder &&

View File

@ -9,6 +9,11 @@ const StyledHistoryList = styled.div`
`;
const StyledHistorySubtitle = styled.div`
position: sticky;
background: ${(props) => props.theme.infoPanel.backgroundColor};
top: 80px;
z-index: 100;
padding: 8px 0 12px;
font-weight: 600;
font-size: 13px;
@ -46,7 +51,11 @@ const StyledHistoryBlock = styled.div`
display: flex;
gap: 8px;
padding: 16px 0;
border-bottom: ${(props) => `solid 1px ${props.theme.infoPanel.borderColor}`};
${({ withBottomDivider, theme }) =>
withBottomDivider
? ` border-bottom: solid 1px ${theme.infoPanel.borderColor}; `
: ` margin-bottom: 12px; `}
.avatar {
min-width: 32px;

View File

@ -71,7 +71,11 @@ const AccountsItemTitle = ({
)}
</div>
{!!contextOptions.length && (
<ContextMenuButton className="context-button" getData={getData} />
<ContextMenuButton
id="info-accounts-options"
className="context-button"
getData={getData}
/>
)}
</StyledAccountsItemTitle>
);

View File

@ -71,6 +71,7 @@ const ItemContextOptions = ({
/>
{options?.length > 0 && (
<ContextMenuButton
id="info-options"
className="expandButton"
title={"Show item actions"}
onClick={onContextMenu}

View File

@ -35,7 +35,7 @@ const ItemTitle = ({
const filesItemSelection =
isRooms &&
!isSeveralItems &&
roomsView === "members" &&
roomsView === "info_members" &&
!selection.isRoom &&
!!selectionParentRoom
? selectionParentRoom

View File

@ -58,18 +58,21 @@ const Accounts = ({
const options = [];
const adminOption = {
id: "info-account-type_docspace-admin",
key: "admin",
title: t("Common:DocSpaceAdmin"),
label: t("Common:DocSpaceAdmin"),
action: "admin",
};
const managerOption = {
id: "info-account-type_room-admin",
key: "manager",
title: t("Common:RoomAdmin"),
label: t("Common:RoomAdmin"),
action: "manager",
};
const userOption = {
id: "info-account-type_user",
key: "user",
title: t("Common:User"),
label: t("Common:User"),
@ -99,6 +102,7 @@ const Accounts = ({
const combobox = (
<ComboBox
id="info-account-type-select"
className="type-combobox"
selectedOption={
typesOptions.find((option) => option.key === role) || {}

View File

@ -85,7 +85,11 @@ const Details = ({
<StyledProperties>
{itemProperties.map((property) => {
return (
<div key={property.title} className="property">
<div
id={property.id}
key={property.title}
className={`property ${property.className}`}
>
<Text className="property-title">{property.title}</Text>
{property.content}
</div>

View File

@ -0,0 +1,93 @@
import React from "react";
import Avatar from "@docspace/components/avatar";
import Text from "@docspace/components/text";
import HistoryBlockMessage from "./HistoryBlockMessage";
import HistoryBlockItemList from "./HistoryBlockItemList";
import HistoryBlockUser from "./HistoryBlockUser";
import { FeedItemTypes } from "@docspace/common/constants";
import { StyledHistoryBlock } from "../../styles/history";
import { getDateTime } from "../../helpers/HistoryHelper";
const HistoryBlock = ({
t,
selection,
feed,
selectedFolder,
selectionParentRoom,
getInfoPanelItemIcon,
checkAndOpenLocationAction,
openUser,
isVisitor,
isLastEntity,
}) => {
const { target, initiator, json, groupedFeeds } = feed;
const isUserAction = json.Item === FeedItemTypes.User && target;
const isItemAction =
json.Item === FeedItemTypes.File || json.Item === FeedItemTypes.Folder;
return (
<StyledHistoryBlock
withBottomDivider={!isLastEntity}
isUserAction={isUserAction}
>
<Avatar
role="user"
className="avatar"
size="min"
source={
initiator.avatarSmall ||
(initiator.displayName
? ""
: initiator.email && "/static/images/@.react.svg")
}
userName={initiator.displayName}
/>
<div className="info">
<div className="title">
<Text className="name">{initiator.displayName}</Text>
{initiator.isOwner && (
<Text className="secondary-info">
{t("Common:Owner").toLowerCase()}
</Text>
)}
<Text className="date">{getDateTime(json.ModifiedDate)}</Text>
</div>
<HistoryBlockMessage
t={t}
className="message"
action={json}
groupedActions={groupedFeeds}
selection={selection}
selectedFolder={selectedFolder}
selectionParentRoom={selectionParentRoom}
/>
{isItemAction && (
<HistoryBlockItemList
t={t}
items={[json, ...groupedFeeds]}
getInfoPanelItemIcon={getInfoPanelItemIcon}
checkAndOpenLocationAction={checkAndOpenLocationAction}
/>
)}
{isUserAction &&
[target, ...groupedFeeds].map((user, i) => (
<HistoryBlockUser
isVisitor={isVisitor}
key={user.id}
user={user}
withComma={i !== groupedFeeds.length}
openUser={openUser}
/>
))}
</div>
</StyledHistoryBlock>
);
};
export default HistoryBlock;

View File

@ -2,25 +2,14 @@ import React, { useState, useEffect, useRef } from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import {
StyledHistoryBlock,
StyledHistoryList,
StyledHistorySubtitle,
} from "../../styles/history";
import { StyledHistoryList, StyledHistorySubtitle } from "../../styles/history";
import Avatar from "@docspace/components/avatar";
import Text from "@docspace/components/text";
import { parseAndFormatDate } from "../../helpers/DetailsHelper";
import HistoryBlockMessage from "./HistoryBlockMessage";
import HistoryBlockItemList from "./HistoryBlockItemList";
import Loaders from "@docspace/common/components/Loaders";
import HistoryBlockUser from "./HistoryBlockUser";
import { FeedItemTypes } from "@docspace/common/constants";
import { getRelativeDateDay } from "./../../helpers/HistoryHelper";
import HistoryBlock from "./HistoryBlock";
const History = ({
t,
personal,
culture,
selection,
selectedFolder,
selectionParentRoom,
@ -59,9 +48,11 @@ const History = ({
const parseHistoryJSON = (fetchedHistory) => {
let feeds = fetchedHistory.feeds;
let newFeeds = [];
let parsedFeeds = [];
for (let i = 0; i < feeds.length; i++) {
const feedsJSON = JSON.parse(feeds[i].json);
const feedDay = getRelativeDateDay(t, feeds[i].modifiedDate);
let newGroupedFeeds = [];
if (feeds[i].groupedFeeds) {
@ -74,14 +65,26 @@ const History = ({
);
}
newFeeds.push({
if (parsedFeeds.length && parsedFeeds.at(-1).day === feedDay)
parsedFeeds.at(-1).feeds.push({
...feeds[i],
json: feedsJSON,
groupedFeeds: newGroupedFeeds,
});
else
parsedFeeds.push({
day: feedDay,
feeds: [
{
...feeds[i],
json: feedsJSON,
groupedFeeds: newGroupedFeeds,
},
],
});
}
return { ...fetchedHistory, feeds: newFeeds };
return { ...fetchedHistory, feedsByDays: parsedFeeds };
};
useEffect(async () => {
@ -101,76 +104,24 @@ const History = ({
return (
<>
<StyledHistoryList>
<StyledHistorySubtitle>{t("RecentActivities")}</StyledHistorySubtitle>
{history.feeds.map((feed) => (
<StyledHistoryBlock
{history.feedsByDays.map(({ day, feeds }) => [
<StyledHistorySubtitle key={day}>{day}</StyledHistorySubtitle>,
...feeds.map((feed, i) => (
<HistoryBlock
key={feed.json.Id}
isUserAction={feed.json.Item === FeedItemTypes.User && feed.target}
>
<Avatar
role="user"
className="avatar"
size="min"
source={
feed.initiator.avatarSmall ||
(feed.initiator.displayName
? ""
: feed.initiator.email && "/static/images/@.react.svg")
}
userName={feed.initiator.displayName}
/>
<div className="info">
<div className="title">
<Text className="name">{feed.initiator.displayName}</Text>
{feed.initiator.isOwner && (
<Text className="secondary-info">
{t("Common:Owner").toLowerCase()}
</Text>
)}
<Text className="date">
{parseAndFormatDate(
feed.json.ModifiedDate,
personal,
culture
)}
</Text>
</div>
<HistoryBlockMessage
t={t}
className="message"
action={feed.json}
groupedActions={feed.groupedFeeds}
feed={feed}
selection={selection}
selectedFolder={selectedFolder}
selectionParentRoom={selectionParentRoom}
/>
{(feed.json.Item === FeedItemTypes.File ||
feed.json.Item === FeedItemTypes.Folder) && (
<HistoryBlockItemList
t={t}
items={[feed.json, ...feed.groupedFeeds]}
getInfoPanelItemIcon={getInfoPanelItemIcon}
checkAndOpenLocationAction={checkAndOpenLocationAction}
/>
)}
{feed.json.Item === FeedItemTypes.User &&
feed.target &&
[feed.target, ...feed.groupedFeeds].map((user, i) => (
<HistoryBlockUser
isVisitor={isVisitor}
key={user.id}
user={user}
withComma={i !== feed.groupedFeeds.length}
openUser={openUser}
isVisitor={isVisitor}
isLastEntity={i === feeds.length - 1}
/>
))}
</div>
</StyledHistoryBlock>
))}
)),
])}
</StyledHistoryList>
</>
);

View File

@ -130,6 +130,7 @@ const Members = ({
</Text>
{canInviteUserInRoomAbility && (
<IconButton
id="info_add-user"
className={"icon"}
title={t("Common:AddUsers")}
iconName="/static/images/person+.react.svg"

View File

@ -47,27 +47,27 @@ const InfoPanelHeaderContent = (props) => {
const closeInfoPanel = () => setIsVisible(false);
const setMembers = () => setView("members");
const setHistory = () => setView("history");
const setDetails = () => setView("details");
const setMembers = () => setView("info_members");
const setHistory = () => setView("info_history");
const setDetails = () => setView("info_details");
const isArchiveRoot = rootFolderType === FolderType.Archive;
const submenuData = [
{
id: "members",
id: "info_members",
name: t("InfoPanel:SubmenuMembers"),
onClick: setMembers,
content: null,
},
{
id: "history",
id: "info_history",
name: t("InfoPanel:SubmenuHistory"),
onClick: setHistory,
content: null,
},
{
id: "details",
id: "info_details",
name: t("InfoPanel:SubmenuDetails"),
onClick: setDetails,
content: null,
@ -100,6 +100,7 @@ const InfoPanelHeaderContent = (props) => {
{!isTablet && (
<div className="info-panel-toggle-bg">
<IconButton
id="info-panel-toggle--close"
className="info-panel-toggle"
iconName="images/panel.react.svg"
size="16"

View File

@ -136,35 +136,41 @@ class SectionHeaderContent extends React.Component {
]
: [
{
id: "personal_new-documnet",
key: "new-document",
label: t("NewDocument"),
onClick: this.createDocument,
icon: "images/actions.documents.react.svg",
},
{
id: "personal_new-spreadsheet",
key: "new-spreadsheet",
label: t("NewSpreadsheet"),
onClick: this.createSpreadsheet,
icon: "images/spreadsheet.react.svg",
},
{
id: "personal_new-presentation",
key: "new-presentation",
label: t("NewPresentation"),
onClick: this.createPresentation,
icon: "images/actions.presentation.react.svg",
},
{
id: "personal_form-template",
icon: "images/form.react.svg",
label: t("Translations:NewForm"),
key: "new-form-base",
items: [
{
id: "personal_template_black",
key: "new-form",
label: t("Translations:SubNewForm"),
icon: "images/form.blank.react.svg",
onClick: this.createForm,
},
{
id: "personal_template_new-form-file",
key: "new-form-file",
label: t("Translations:SubNewFormFile"),
icon: "images/form.file.react.svg",
@ -172,6 +178,7 @@ class SectionHeaderContent extends React.Component {
disabled: isPrivacyFolder,
},
{
id: "personal_template_oforms-gallery",
key: "oforms-gallery",
label: t("Common:OFORMsGallery"),
icon: "images/form.gallery.react.svg",
@ -181,6 +188,7 @@ class SectionHeaderContent extends React.Component {
],
},
{
id: "personal_new-folder",
key: "new-folder",
label: t("NewFolder"),
onClick: this.createFolder,
@ -355,6 +363,7 @@ class SectionHeaderContent extends React.Component {
if (isArchiveFolder) {
return [
{
id: "header_option_empty-archive",
key: "empty-archive",
label: t("ArchiveAction"),
onClick: this.onEmptyTrashAction,
@ -362,6 +371,7 @@ class SectionHeaderContent extends React.Component {
icon: "images/clear.trash.react.svg",
},
{
id: "header_option_restore-all",
key: "restore-all",
label: t("RestoreAll"),
onClick: this.onRestoreAllArchiveAction,
@ -373,6 +383,7 @@ class SectionHeaderContent extends React.Component {
return [
{
id: "header_option_sharing-settings",
key: "sharing-settings",
label: t("SharingPanel:SharingSettingsTitle"),
onClick: this.onOpenSharingPanel,
@ -380,6 +391,7 @@ class SectionHeaderContent extends React.Component {
icon: "/static/images/share.react.svg",
},
{
id: "header_option_link-portal-users",
key: "link-portal-users",
label: t("LinkForPortalUsers"),
onClick: this.createLinkForPortalUsers,
@ -387,6 +399,7 @@ class SectionHeaderContent extends React.Component {
icon: "/static/images/invitation.link.react.svg",
},
{
id: "header_option_empty-trash",
key: "empty-trash",
label: t("RecycleBinAction"),
onClick: this.onEmptyTrashAction,
@ -394,6 +407,7 @@ class SectionHeaderContent extends React.Component {
icon: "images/clear.trash.react.svg",
},
{
id: "header_option_restore-all",
key: "restore-all",
label: t("RestoreAll"),
onClick: this.onRestoreAllAction,
@ -401,6 +415,7 @@ class SectionHeaderContent extends React.Component {
icon: "images/subtract.react.svg",
},
{
id: "header_option_show-info",
key: "show-info",
label: t("InfoPanel:ViewDetails"),
onClick: this.onShowInfo,
@ -408,6 +423,7 @@ class SectionHeaderContent extends React.Component {
icon: "/static/images/info.outline.react.svg",
},
{
id: "header_option_reconnect-storage",
key: "reconnect-storage",
label: t("Common:ReconnectStorage"),
icon: "images/reconnect.svg",
@ -415,6 +431,7 @@ class SectionHeaderContent extends React.Component {
disabled: !selectedFolder.providerKey || !isRoom,
},
{
id: "header_option_edit-room",
key: "edit-room",
label: t("EditRoom"),
icon: "images/settings.react.svg",
@ -422,6 +439,7 @@ class SectionHeaderContent extends React.Component {
disabled: !isRoom,
},
{
id: "header_option_invite-users-to-room",
key: "invite-users-to-room",
label: t("InviteUsers"),
icon: "/static/images/person.react.svg",
@ -429,14 +447,21 @@ class SectionHeaderContent extends React.Component {
disabled: !isRoom,
},
{
id: "header_option_room-info",
key: "room-info",
label: t("Common:Info"),
icon: "/static/images/info.outline.react.svg",
onClick: this.onToggleInfoPanel,
disabled: !isRoom,
},
{ key: "separator-2", isSeparator: true, disabled: isRecycleBinFolder },
{
id: "header_option_separator-2",
key: "separator-2",
isSeparator: true,
disabled: isRecycleBinFolder,
},
{
id: "header_option_archive-room",
key: "archive-room",
label: t("Archived"),
icon: "/static/images/room.archive.svg",
@ -446,6 +471,7 @@ class SectionHeaderContent extends React.Component {
action: "archive",
},
{
id: "header_option_download",
key: "download",
label: t("Common:Download"),
onClick: this.downloadAction,
@ -453,6 +479,7 @@ class SectionHeaderContent extends React.Component {
icon: "images/download.react.svg",
},
{
id: "header_option_move-to",
key: "move-to",
label: t("MoveTo"),
onClick: this.onMoveAction,
@ -460,6 +487,7 @@ class SectionHeaderContent extends React.Component {
icon: "images/move.react.svg",
},
{
id: "header_option_copy",
key: "copy",
label: t("Translations:Copy"),
onClick: this.onCopyAction,
@ -467,6 +495,7 @@ class SectionHeaderContent extends React.Component {
icon: "/static/images/copy.react.svg",
},
{
id: "header_option_rename",
key: "rename",
label: t("Rename"),
onClick: this.renameAction,
@ -474,11 +503,13 @@ class SectionHeaderContent extends React.Component {
icon: "images/rename.react.svg",
},
{
id: "header_option_separator-3",
key: "separator-3",
isSeparator: true,
disabled: isDisabled,
},
{
id: "header_option_delete",
key: "delete",
label: t("Common:Delete"),
onClick: this.onDeleteAction,
@ -506,14 +537,20 @@ class SectionHeaderContent extends React.Component {
};
getMenuItems = () => {
const { t, cbMenuItems, getCheckboxItemLabel } = this.props;
const {
t,
cbMenuItems,
getCheckboxItemLabel,
getCheckboxItemId,
} = this.props;
const checkboxOptions = (
<>
{cbMenuItems.map((key) => {
const label = getCheckboxItemLabel(t, key);
const id = getCheckboxItemId(key);
return (
<DropDownItem
id={id}
key={key}
label={label}
data-key={key}
@ -700,6 +737,7 @@ export default inject(
isThirdPartySelection,
cbMenuItems,
getCheckboxItemLabel,
getCheckboxItemId,
isEmptyFilesList,
getFolderInfo,
setBufferSelection,
@ -817,6 +855,7 @@ export default inject(
getHeaderMenu,
backToParentFolder,
getCheckboxItemLabel,
getCheckboxItemId,
setSelectFileDialogVisible,
isRecycleBinFolder,

View File

@ -159,6 +159,7 @@ const StyledAutoBackup = styled.div`
}
.auto-backup_badge {
height: 16px;
margin-left: 8px;
}
${(props) => !props.isEnableAuto && UnavailableStyles}

View File

@ -22,8 +22,6 @@ const Backup = ({
return (
<>
<HelpButton
displayType="auto"
place="bottom"
iconName={"/static/images/help.react.svg"}
tooltipContent={
<>

View File

@ -39,6 +39,7 @@ class AccountsContextOptionsStore {
case "profile":
return {
id: "option_profile",
key: option,
icon: "/static/images/profile.react.svg",
label: t("Common:Profile"),
@ -47,6 +48,7 @@ class AccountsContextOptionsStore {
case "change-name":
return {
id: "option_change-name",
key: option,
icon: "images/pencil.react.svg",
label: t("PeopleTranslations:NameChangeButton"),
@ -54,6 +56,7 @@ class AccountsContextOptionsStore {
};
case "change-email":
return {
id: "option_change-email",
key: option,
icon: "images/change.mail.react.svg",
label: t("PeopleTranslations:EmailChangeButton"),
@ -61,6 +64,7 @@ class AccountsContextOptionsStore {
};
case "change-password":
return {
id: "option_change-password",
key: option,
icon: "images/change.security.react.svg",
label: t("PeopleTranslations:PasswordChangeButton"),
@ -68,6 +72,7 @@ class AccountsContextOptionsStore {
};
case "change-owner":
return {
id: "option_change-owner",
key: option,
icon: "/static/images/refresh.react.svg",
label: t("Translations:OwnerChange"),
@ -76,6 +81,7 @@ class AccountsContextOptionsStore {
case "enable":
return {
id: "option_enable",
key: option,
icon: "images/enable.react.svg",
label: t("PeopleTranslations:EnableUserButton"),
@ -83,6 +89,7 @@ class AccountsContextOptionsStore {
};
case "disable":
return {
id: "option_disable",
key: option,
icon: "images/remove.react.svg",
label: t("PeopleTranslations:DisableUserButton"),
@ -91,6 +98,7 @@ class AccountsContextOptionsStore {
case "reassign-data":
return {
id: "option_reassign-data",
key: option,
icon: "images/ressing_data.react.svg",
label: t("PeopleTranslations:ReassignData"),
@ -98,6 +106,7 @@ class AccountsContextOptionsStore {
};
case "delete-personal-data":
return {
id: "option_delete-personal-data",
key: option,
icon: "images/del_data.react.svg",
label: t("PeopleTranslations:RemoveData"),
@ -105,6 +114,7 @@ class AccountsContextOptionsStore {
};
case "delete-user":
return {
id: "option_delete-user",
key: option,
icon: "images/trash.react.svg",
label: t("DeleteProfileEverDialog:DeleteUser"),
@ -113,6 +123,7 @@ class AccountsContextOptionsStore {
case "details":
return {
id: "option_details",
key: option,
icon: "images/info.react.svg",
label: t("Common:Info"),
@ -121,6 +132,7 @@ class AccountsContextOptionsStore {
case "invite-again":
return {
id: "option_invite-again",
key: option,
icon: "/static/images/invite.again.react.svg",
label: t("LblInviteAgain"),
@ -128,6 +140,7 @@ class AccountsContextOptionsStore {
};
case "reset-auth":
return {
id: "option_reset-auth",
key: option,
icon: "images/restore.auth.react.svg",
label: t("PeopleTranslations:ResetAuth"),

View File

@ -469,6 +469,7 @@ class ContextOptionsStore {
const blockAction = isCanWebEdit
? {
id: "option_block-unblock-version",
key: "block-unblock-version",
label: t("UnblockVersion"),
icon: "/static/images/locked.react.svg",
@ -486,6 +487,7 @@ class ContextOptionsStore {
? onlyShowVersionHistory
? [
{
id: "option_show-version-history",
key: "show-version-history",
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
@ -495,11 +497,13 @@ class ContextOptionsStore {
]
: [
{
id: "option_version",
key: "version",
label: t("VersionHistory"),
icon: "images/history-finalized.react.svg",
items: [
{
id: "option_finalize-version",
key: "finalize-version",
label: t("FinalizeVersion"),
icon: "images/history-finalized.react.svg",
@ -507,6 +511,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_version-history",
key: "show-version-history",
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
@ -519,6 +524,7 @@ class ContextOptionsStore {
]
: [
{
id: "option_finalize-version",
key: "finalize-version",
label: t("FinalizeVersion"),
icon: "images/history-finalized.react.svg",
@ -526,6 +532,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_version-history",
key: "show-version-history",
label: t("ShowVersionHistory"),
icon: "images/history.react.svg",
@ -539,11 +546,13 @@ class ContextOptionsStore {
!isMobile && !isMobileUtils() && !isTabletUtils()
? [
{
id: "option_move-or-copy",
key: "move",
label: t("MoveOrCopy"),
icon: "/static/images/copy.react.svg",
items: [
{
id: "option_move-to",
key: "move-to",
label: t("MoveTo"),
icon: "images/move.react.svg",
@ -551,6 +560,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_copy-to",
key: "copy-to",
label: t("Translations:Copy"),
icon: "/static/images/copy.react.svg",
@ -558,6 +568,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_create-copy",
key: "copy",
label: t("Common:Duplicate"),
icon: "/static/images/duplicate.react.svg",
@ -569,6 +580,7 @@ class ContextOptionsStore {
]
: [
{
id: "option_move-to",
key: "move-to",
label: t("MoveTo"),
icon: "images/move.react.svg",
@ -576,6 +588,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_copy-to",
key: "copy-to",
label: t("Translations:Copy"),
icon: "/static/images/copy.react.svg",
@ -583,6 +596,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_create-copy",
key: "copy",
label: t("Common:Duplicate"),
icon: "/static/images/duplicate.react.svg",
@ -593,6 +607,7 @@ class ContextOptionsStore {
const optionsModel = [
{
id: "option_select",
key: "select",
label: "Select",
icon: "images/check-box.react.svg",
@ -600,6 +615,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_open",
key: "open",
label: t("Open"),
icon: "images/folder.react.svg",
@ -607,6 +623,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_fill-form",
key: "fill-form",
label: t("Common:FillFormButton"),
icon: "/static/images/form.fill.rect.svg",
@ -614,6 +631,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_edit",
key: "edit",
label: t("Common:EditButton"),
icon: "/static/images/access.edit.react.svg",
@ -621,6 +639,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_preview",
key: "preview",
label: t("Preview"),
icon: "/static/images/eye.react.svg",
@ -628,6 +647,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_view",
key: "view",
label: t("Common:View"),
icon: "/static/images/eye.react.svg",
@ -635,6 +655,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_make-form",
key: "make-form",
label: t("Common:MakeForm"),
icon: "/static/images/form.plus.react.svg",
@ -646,6 +667,7 @@ class ContextOptionsStore {
isSeparator: true,
},
{
id: "option_reconnect-storage",
key: "reconnect-storage",
label: t("Common:ReconnectStorage"),
icon: "images/reconnect.svg",
@ -653,6 +675,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_edit-room",
key: "edit-room",
label: t("EditRoom"),
icon: "images/settings.react.svg",
@ -660,6 +683,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_invite-users-to-room",
key: "invite-users-to-room",
label: t("InviteUsers"),
icon: "/static/images/person.react.svg",
@ -668,6 +692,7 @@ class ContextOptionsStore {
action: item.id,
},
{
id: "option_room-info",
key: "room-info",
label: t("Common:Info"),
icon: "/static/images/info.outline.react.svg",
@ -675,6 +700,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_pin-room",
key: "pin-room",
label: t("Pin"),
icon: "/static/images/pin.react.svg",
@ -684,6 +710,7 @@ class ContextOptionsStore {
action: "pin",
},
{
id: "option_unpin-room",
key: "unpin-room",
label: t("Unpin"),
icon: "/static/images/unpin.react.svg",
@ -693,6 +720,7 @@ class ContextOptionsStore {
action: "unpin",
},
{
id: "option_sharing-settings",
key: "sharing-settings",
label: t("SharingPanel:SharingSettingsTitle"),
icon: "/static/images/share.react.svg",
@ -700,6 +728,7 @@ class ContextOptionsStore {
disabled: !isShareable,
},
{
id: "option_owner-change",
key: "owner-change",
label: t("Translations:OwnerChange"),
icon: "images/file.actions.owner.react.svg",
@ -707,6 +736,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_link-for-portal-users",
key: "link-for-portal-users",
label: t("LinkForPortalUsers"),
icon: "/static/images/invitation.link.react.svg",
@ -714,6 +744,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_send-by-email",
key: "send-by-email",
label: t("SendByEmail"),
icon: "/static/images/mail.react.svg",
@ -721,6 +752,7 @@ class ContextOptionsStore {
},
...versionActions,
{
id: "option_show-info",
key: "show-info",
label: t("Common:Info"),
icon: "/static/images/info.outline.react.svg",
@ -733,6 +765,7 @@ class ContextOptionsStore {
isSeparator: true,
},
{
id: "option_open-location",
key: "open-location",
label: t("OpenLocation"),
icon: "images/folder.location.react.svg",
@ -740,6 +773,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_mark-read",
key: "mark-read",
label: t("MarkRead"),
icon: "images/tick.rounded.svg",
@ -747,6 +781,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_mark-as-favorite",
key: "mark-as-favorite",
label: t("MarkAsFavorite"),
icon: "images/favorites.react.svg",
@ -756,6 +791,7 @@ class ContextOptionsStore {
action: "mark",
},
{
id: "option_remove-from-favorites",
key: "remove-from-favorites",
label: t("RemoveFromFavorites"),
icon: "images/favorites.react.svg",
@ -765,6 +801,7 @@ class ContextOptionsStore {
action: "remove",
},
{
id: "option_download",
key: "download",
label: t("Common:Download"),
icon: "images/download.react.svg",
@ -772,6 +809,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_download-as",
key: "download-as",
label: t("Translations:DownloadAs"),
icon: "images/download-as.react.svg",
@ -780,6 +818,7 @@ class ContextOptionsStore {
},
...moveActions,
{
id: "option_restore",
key: "restore",
label: t("Common:Restore"),
icon: "images/move.react.svg",
@ -787,6 +826,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_rename",
key: "rename",
label: t("Rename"),
icon: "images/rename.react.svg",
@ -798,6 +838,7 @@ class ContextOptionsStore {
isSeparator: true,
},
{
id: "option_unsubscribe",
key: "unsubscribe",
label: t("RemoveFromList"),
icon: "images/remove.svg",
@ -805,6 +846,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_change-thirdparty-info",
key: "change-thirdparty-info",
label: t("Translations:ThirdPartyInfo"),
icon: "/static/images/access.edit.react.svg",
@ -812,6 +854,7 @@ class ContextOptionsStore {
disabled: false,
},
{
id: "option_archive-room",
key: "archive-room",
label: t("Archived"),
icon: "/static/images/room.archive.svg",
@ -821,6 +864,7 @@ class ContextOptionsStore {
action: "archive",
},
{
id: "option_unarchive-room",
key: "unarchive-room",
label: t("Common:Restore"),
icon: "images/subtract.react.svg",
@ -830,6 +874,7 @@ class ContextOptionsStore {
action: "unarchive",
},
{
id: "option_delete",
key: "delete",
label: isRootThirdPartyFolder
? t("Common:Disconnect")

View File

@ -1072,7 +1072,6 @@ class FilesActionStore {
openLocationAction = async (locationId) => {
this.filesStore.setBufferSelection(null);
const files = await this.filesStore.fetchFiles(locationId, null);
console.log(files);
return files;
};
@ -1439,6 +1438,7 @@ class FilesActionStore {
if (!isTablet() && !isMobile) return null;
else
return {
id: "menu-show-info",
key: "show-info",
label: t("Common:Info"),
iconUrl: "/static/images/info.outline.react.svg",
@ -1448,6 +1448,7 @@ class FilesActionStore {
if (!this.isAvailableOption("copy")) return null;
else
return {
id: "menu-copy",
label: t("Translations:Copy"),
onClick: () => setCopyPanelVisible(true),
iconUrl: "/static/images/copyTo.react.svg",
@ -1457,6 +1458,7 @@ class FilesActionStore {
if (!this.isAvailableOption("download")) return null;
else
return {
id: "menu-download",
label: t("Common:Download"),
onClick: () =>
this.downloadAction(
@ -1469,6 +1471,7 @@ class FilesActionStore {
if (!this.isAvailableOption("downloadAs")) return null;
else
return {
id: "menu-download-as",
label: t("Translations:DownloadAs"),
onClick: () => setDownloadDialogVisible(true),
iconUrl: "/static/images/downloadAs.react.svg",
@ -1478,12 +1481,14 @@ class FilesActionStore {
if (!this.isAvailableOption("moveTo")) return null;
else
return {
id: "menu-move-to",
label: t("MoveTo"),
onClick: () => setMoveToPanelVisible(true),
iconUrl: "/static/images/move.react.svg",
};
case "pin":
return {
id: "menu-pin",
key: "pin",
label: t("Pin"),
iconUrl: "/static/images/pin.react.svg",
@ -1492,6 +1497,7 @@ class FilesActionStore {
};
case "unpin":
return {
id: "menu-unpin",
key: "unpin",
label: t("Unpin"),
iconUrl: "/static/images/unpin.react.svg",
@ -1502,6 +1508,7 @@ class FilesActionStore {
if (!this.isAvailableOption("archive")) return null;
else
return {
id: "menu-archive",
key: "archive",
label: t("Archived"),
iconUrl: "/static/images/room.archive.svg",
@ -1512,6 +1519,7 @@ class FilesActionStore {
if (!this.isAvailableOption("unarchive")) return null;
else
return {
id: "menu-unarchive",
key: "unarchive",
label: t("Common:Restore"),
iconUrl: "images/subtract.react.svg",
@ -1522,6 +1530,7 @@ class FilesActionStore {
if (!this.isAvailableOption("delete-room")) return null;
else
return {
id: "menu-delete-room",
label: t("Common:Delete"),
onClick: () => this.deleteRooms(t),
iconUrl: "/static/images/delete.react.svg",
@ -1531,6 +1540,7 @@ class FilesActionStore {
if (!this.isAvailableOption("delete")) return null;
else
return {
id: "menu-delete",
label: t("Common:Delete"),
onClick: () => {
if (this.settingsStore.confirmDelete) {
@ -1700,6 +1710,7 @@ class FilesActionStore {
.set("download", download)
.set("downloadAs", downloadAs)
.set("restore", {
id: "menu-restore",
label: t("Common:Restore"),
onClick: () => setMoveToPanelVisible(true),
iconUrl: "/static/images/move.react.svg",

View File

@ -2216,6 +2216,42 @@ class FilesStore {
}
};
getCheckboxItemId = (key) => {
switch (key) {
case "all":
return "selected-all";
case FilterType.FoldersOnly:
return "selected-only-folders";
case FilterType.DocumentsOnly:
return "selected-only-documents";
case FilterType.PresentationsOnly:
return "selected-only-presentations";
case FilterType.SpreadsheetsOnly:
return "selected-only-spreadsheets";
case FilterType.ImagesOnly:
return "selected-only-images";
case FilterType.MediaOnly:
return "selected-only-media";
case FilterType.ArchiveOnly:
return "selected-only-archives";
case FilterType.FilesOnly:
return "selected-only-files";
case `room-${RoomsType.FillingFormsRoom}`:
return "selected-only-filling-form-rooms";
case `room-${RoomsType.CustomRoom}`:
return "selected-only-custom-room";
case `room-${RoomsType.EditingRoom}`:
return "selected-only-collaboration-rooms";
case `room-${RoomsType.ReviewRoom}`:
return "selected-only-review-rooms";
case `room-${RoomsType.ReadOnlyRoom}`:
return "selected-only-view-rooms";
default:
return "";
}
};
get sortedFiles() {
const {
extsConvertible,

View File

@ -35,7 +35,18 @@ class HeaderMenuStore {
return cbMenu;
}
getMenuItemId = (item) => {
switch (item) {
case "active":
return "selected_active";
case "pending":
return "selected_pending";
case "disabled":
return "selected_disabled";
case "all":
return "selected_all";
}
};
getCheckboxItemLabel = (t, item) => {
switch (item) {
case "active":

View File

@ -193,7 +193,7 @@ class PeopleStore {
const options = [];
const adminOption = {
id: "group-menu_administrator",
id: "menu_change-user_administrator",
className: "group-menu_drop-down",
label: t("Common:DocSpaceAdmin"),
title: t("Common:DocSpaceAdmin"),
@ -202,7 +202,7 @@ class PeopleStore {
key: "administrator",
};
const managerOption = {
id: "group-menu_manager",
id: "menu_change-user_manager",
className: "group-menu_drop-down",
label: t("Common:RoomAdmin"),
title: t("Common:RoomAdmin"),
@ -211,7 +211,7 @@ class PeopleStore {
key: "manager",
};
const userOption = {
id: "group-menu_user",
id: "menu_change-user_user",
className: "group-menu_drop-down",
label: t("Common:User"),
title: t("Common:User"),
@ -228,6 +228,7 @@ class PeopleStore {
const headerMenu = [
{
id: "menu-change-type",
key: "change-user",
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
disabled: !hasUsersToMakeEmployees,
@ -236,6 +237,7 @@ class PeopleStore {
options: options,
},
{
id: "menu-info",
key: "info",
label: t("Common:Info"),
disabled:
@ -245,6 +247,7 @@ class PeopleStore {
iconUrl: "images/info.react.svg",
},
{
id: "menu-invite",
key: "invite",
label: t("Common:Invite"),
disabled: !hasUsersToInvite,
@ -252,6 +255,7 @@ class PeopleStore {
iconUrl: "/static/images/invite.again.react.svg",
},
{
id: "menu-enable",
key: "enable",
label: t("Common:Enable"),
disabled: !hasUsersToActivate,
@ -259,6 +263,7 @@ class PeopleStore {
iconUrl: "images/enable.react.svg",
},
{
id: "menu-disable",
key: "disable",
label: t("PeopleTranslations:DisableUserButton"),
disabled: !hasUsersToDisable,
@ -266,6 +271,7 @@ class PeopleStore {
iconUrl: "images/disable.react.svg",
},
{
id: "menu-delete",
key: "delete",
label: t("Common:Delete"),
disabled: !hasUsersToRemove,

View File

@ -195,6 +195,7 @@ const Navigation = ({
</StyledContainer>
{infoPanelIsVisible && (
<ToggleInfoPanelButton
id="info-panel-toggle--open"
isRootFolder={isRootFolder}
toggleInfoPanel={toggleInfoPanel}
isInfoPanelVisible={isInfoPanelVisible}

View File

@ -47,6 +47,9 @@ const StyledContainer = styled.div`
width: 100%;
grid-template-columns: ${(props) =>
props.isRootFolder ? "auto 1fr" : "29px 1fr auto"};
display: flex;
align-items: center;
justify-content: space-between;
`}
`;

View File

@ -125,6 +125,7 @@ const ControlButtons = ({
)}
{!personal && (
<ContextMenuButton
id="header_optional-button"
zIndex={402}
className="option-button"
directionX="right"
@ -147,6 +148,7 @@ const ControlButtons = ({
<>
{!isMobile && (
<PlusButton
id="header_add-button"
className="add-button"
getData={getContextOptionsPlus}
withMenu={withMenu}

View File

@ -31,6 +31,7 @@ const PlusButton = (props) => {
<IconButton
onClick={onClick}
iconName="images/plus.svg"
id={props.id}
size={15}
isFill
/>
@ -50,6 +51,7 @@ PlusButton.propTypes = {
className: PropTypes.string,
getData: PropTypes.func.isRequired,
onPlusClick: PropTypes.func,
id: PropTypes.string,
};
PlusButton.defaultProps = {

View File

@ -54,6 +54,7 @@ const ToggleInfoPanelButton = ({
isRootFolder,
isInfoPanelVisible,
toggleInfoPanel,
id,
}) => {
return (
<StyledInfoPanelToggleWrapper
@ -62,6 +63,7 @@ const ToggleInfoPanelButton = ({
>
<div className="info-panel-toggle-bg">
<IconButton
id={id}
className="info-panel-toggle"
iconName="images/panel.react.svg"
size="16"

View File

@ -23,8 +23,8 @@ class InfoPanelStore {
selection = null;
selectionParentRoom = null;
roomsView = "details";
fileView = "history";
roomsView = "info_details";
fileView = "info_history";
authStore = null;
settingsStore = null;
@ -54,7 +54,7 @@ class InfoPanelStore {
setView = (view) => {
this.roomsView = view;
this.fileView = view === "members" ? "history" : view;
this.fileView = view === "info_members" ? "info_history" : view;
};
// Selection helpers //

View File

@ -222,7 +222,7 @@ class ContextMenuButton extends React.Component {
(item.label || item.icon || item.key) && (
<DropDownItem
{...item}
id={item.key}
id={item.id}
key={item.key || index}
onClick={this.onDropDownItemClick.bind(this, item)}
/>

View File

@ -113,6 +113,7 @@ class LinkWithDropdown extends React.Component {
const dropDownItem = data.map((item) => (
<DropDownItem
className="drop-down-item"
id={item.key}
key={item.key}
{...item}
onClick={this.onClickDropDownItem}

View File

@ -140,6 +140,7 @@ const GroupMenuItem = ({ item }) => {
title,
withDropDown,
options,
id,
} = item;
return (
<>
@ -148,6 +149,7 @@ const GroupMenuItem = ({ item }) => {
) : (
<>
<StyledButton
id={id}
label={label}
title={title || label}
isDisabled={disabled}

View File

@ -36,6 +36,7 @@ const TableGroupMenu = (props) => {
{...rest}
>
<Checkbox
id="menu-checkbox_selected-all-file"
className="table-container_group-menu-checkbox"
onChange={onCheckboxChange}
isChecked={isChecked}
@ -43,6 +44,7 @@ const TableGroupMenu = (props) => {
title={t("Common:MainHeaderSelectAll")}
/>
<ComboBox
id="menu-combobox"
comboIcon="/static/images/triangle.navigation.down.react.svg"
noBorder
advancedOptions={checkboxOptions}
@ -63,6 +65,7 @@ const TableGroupMenu = (props) => {
<StyledInfoPanelToggleWrapper isInfoPanelVisible={isInfoPanelVisible}>
<div className="info-panel-toggle-bg">
<IconButton
id="info-panel-toggle--open"
className="info-panel-toggle"
iconName="images/panel.react.svg"
size="16"

View File

@ -389,13 +389,13 @@
<EmbeddedResource Update="Resources\FilesUCResource.zh-CN.resx">
<DependentUpon>FilesUCResource.resx</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Update="Resources\FilesCommonResource.az-Latn-AZ.resx">
<EmbeddedResource Update="Resources\FilesCommonResource.az.resx">
<DependentUpon>FilesCommonResource.resx</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Update="Resources\FilesJSResource.az-Latn-AZ.resx">
<EmbeddedResource Update="Resources\FilesJSResource.az.resx">
<DependentUpon>FilesJSResource.resx</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Update="Resources\FilesUCResource.az-Latn-AZ.resx">
<EmbeddedResource Update="Resources\FilesUCResource.az.resx">
<DependentUpon>FilesUCResource.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup>

View File

@ -58,10 +58,10 @@ public interface IFolderDao<T>
Task<Folder<T>> GetRootFolderByFileAsync(T fileId);
IAsyncEnumerable<Folder<T>> GetRoomsAsync(T parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders,
bool withoutTags, bool excludeSubject, ProviderFilter provider);
bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds);
IAsyncEnumerable<Folder<T>> GetRoomsAsync(IEnumerable<T> parentsIds, IEnumerable<T> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders,
bool withoutTags, bool excludeSubject, ProviderFilter provider);
bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds);
/// <summary>
/// Get a list of folders in current folder.

View File

@ -164,7 +164,8 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
return GetFoldersAsync(parentId, default, FilterType.None, false, default, string.Empty);
}
public async IAsyncEnumerable<Folder<int>> GetRoomsAsync(int parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public async IAsyncEnumerable<Folder<int>> GetRoomsAsync(int parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter,
IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || provider != ProviderFilter.None)
{
@ -179,8 +180,8 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
var filesDbContext = _dbContextFactory.CreateDbContext();
var q = GetFolderQuery(filesDbContext, r => r.ParentId == parentId).AsNoTracking();
q = !withSubfolders ? BuildRoomsQuery(filesDbContext, q, filter, tags, subjectId, searchByTags, withoutTags, searchByTypes, false, excludeSubject)
: BuildRoomsWithSubfoldersQuery(filesDbContext, parentId, filter, tags, searchByTags, searchByTypes, withoutTags, excludeSubject, subjectId);
q = !withSubfolders ? BuildRoomsQuery(filesDbContext, q, filter, tags, subjectId, searchByTags, withoutTags, searchByTypes, false, excludeSubject, subjectFilter, subjectEntriesIds)
: BuildRoomsWithSubfoldersQuery(filesDbContext, parentId, filter, tags, searchByTags, searchByTypes, withoutTags, excludeSubject, subjectId, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -194,7 +195,7 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
}
}
public async IAsyncEnumerable<Folder<int>> GetRoomsAsync(IEnumerable<int> parentsIds, IEnumerable<int> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public async IAsyncEnumerable<Folder<int>> GetRoomsAsync(IEnumerable<int> parentsIds, IEnumerable<int> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || provider != ProviderFilter.None)
{
@ -209,8 +210,8 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
var filesDbContext = _dbContextFactory.CreateDbContext();
var q = GetFolderQuery(filesDbContext, f => roomsIds.Contains(f.Id) || (f.CreateBy == _authContext.CurrentAccount.ID && parentsIds.Contains(f.ParentId))).AsNoTracking();
q = !withSubfolders ? BuildRoomsQuery(filesDbContext, q, filter, tags, subjectId, searchByTags, withoutTags, searchByTypes, false, excludeSubject)
: BuildRoomsWithSubfoldersQuery(filesDbContext, roomsIds, filter, tags, searchByTags, searchByTypes, withoutTags, excludeSubject, subjectId);
q = !withSubfolders ? BuildRoomsQuery(filesDbContext, q, filter, tags, subjectId, searchByTags, withoutTags, searchByTypes, false, excludeSubject, subjectFilter, subjectEntriesIds)
: BuildRoomsWithSubfoldersQuery(filesDbContext, roomsIds, filter, tags, searchByTags, searchByTypes, withoutTags, excludeSubject, subjectId, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -1458,8 +1459,21 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
}
private IQueryable<DbFolder> BuildRoomsQuery(FilesDbContext filesDbContext, IQueryable<DbFolder> query, FolderType filterByType, IEnumerable<string> tags, Guid subjectId, bool searchByTags, bool withoutTags,
bool searchByFilter, bool withSubfolders, bool excludeSubject)
bool searchByFilter, bool withSubfolders, bool excludeSubject, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (subjectId != Guid.Empty)
{
if (subjectFilter == SubjectFilter.Owner)
{
query = excludeSubject ? query.Where(f => f.CreateBy != subjectId) : query.Where(f => f.CreateBy == subjectId);
}
else if (subjectFilter == SubjectFilter.Member)
{
query = excludeSubject ? query.Where(f => f.CreateBy != subjectId && !subjectEntriesIds.Contains(f.Id.ToString()))
: query.Where(f => f.CreateBy == subjectId || subjectEntriesIds.Contains(f.Id.ToString()));
}
}
if (searchByFilter)
{
query = query.Where(f => f.FolderType == filterByType);
@ -1471,11 +1485,6 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
.Where(r => r.tag.Type == TagType.Custom).Any(t => t.EntryId == f.Id.ToString()));
}
if (subjectId != Guid.Empty)
{
query = excludeSubject ? query.Where(f => f.CreateBy != subjectId) : query.Where(f => f.CreateBy == subjectId);
}
if (searchByTags && !withSubfolders)
{
query = query.Join(filesDbContext.TagLink, f => f.Id.ToString(), t => t.EntryId, (folder, tag) => new { folder, tag.TagId })
@ -1488,11 +1497,11 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
}
private IQueryable<DbFolder> BuildRoomsWithSubfoldersQuery(FilesDbContext filesDbContext, int parentId, FolderType filterByType, IEnumerable<string> tags, bool searchByTags, bool searchByFilter, bool withoutTags,
bool excludeSubject, Guid subjectId)
bool excludeSubject, Guid subjectId, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
var q1 = GetFolderQuery(filesDbContext, r => r.ParentId == parentId).AsNoTracking();
q1 = BuildRoomsQuery(filesDbContext, q1, filterByType, tags, subjectId, searchByTags, withoutTags, searchByFilter, true, excludeSubject);
q1 = BuildRoomsQuery(filesDbContext, q1, filterByType, tags, subjectId, searchByTags, withoutTags, searchByFilter, true, excludeSubject, subjectFilter, subjectEntriesIds);
if (searchByTags)
{
@ -1520,11 +1529,11 @@ internal class FolderDao : AbstractDao, IFolderDao<int>
}
private IQueryable<DbFolder> BuildRoomsWithSubfoldersQuery(FilesDbContext filesDbContext, IEnumerable<int> roomsIds, FolderType filterByType, IEnumerable<string> tags, bool searchByTags, bool searchByFilter, bool withoutTags,
bool withoutMe, Guid ownerId)
bool withoutMe, Guid ownerId, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
var q1 = GetFolderQuery(filesDbContext, f => roomsIds.Contains(f.Id)).AsNoTracking();
q1 = BuildRoomsQuery(filesDbContext, q1, filterByType, tags, ownerId, searchByTags, withoutTags, searchByFilter, true, withoutMe);
q1 = BuildRoomsQuery(filesDbContext, q1, filterByType, tags, ownerId, searchByTags, withoutTags, searchByFilter, true, withoutMe, subjectFilter, subjectEntriesIds);
if (searchByTags)
{

View File

@ -272,7 +272,8 @@ public class FileStorageService<T> //: IFileStorageService
bool withoutTags = false,
IEnumerable<string> tagNames = null,
bool excludeSubject = false,
ProviderFilter provider = ProviderFilter.None)
ProviderFilter provider = ProviderFilter.None,
SubjectFilter subjectFilter = SubjectFilter.Owner)
{
var subjectId = string.IsNullOrEmpty(subject) ? Guid.Empty : new Guid(subject);
@ -322,7 +323,7 @@ public class FileStorageService<T> //: IFileStorageService
try
{
(entries, total) = await _entryManager.GetEntriesAsync(parent, from, count, filterType, subjectGroup, subjectId, searchText, searchInContent, withSubfolders, orderBy, searchArea,
withoutTags, tagNames, excludeSubject, provider);
withoutTags, tagNames, excludeSubject, provider, subjectFilter);
}
catch (Exception e)
{

View File

@ -62,6 +62,7 @@ public class FileSecurity : IFileSecurity
private readonly AuthManager _authManager;
private readonly GlobalFolder _globalFolder;
private readonly FileSecurityCommon _fileSecurityCommon;
private readonly CoreBaseSettings _coreBaseSettings;
public FileSecurity(
IDaoFactory daoFactory,
@ -70,7 +71,8 @@ public class FileSecurity : IFileSecurity
AuthContext authContext,
AuthManager authManager,
GlobalFolder globalFolder,
FileSecurityCommon fileSecurityCommon)
FileSecurityCommon fileSecurityCommon,
CoreBaseSettings coreBaseSettings)
{
_daoFactory = daoFactory;
_userManager = userManager;
@ -79,6 +81,7 @@ public class FileSecurity : IFileSecurity
_authManager = authManager;
_globalFolder = globalFolder;
_fileSecurityCommon = fileSecurityCommon;
_coreBaseSettings = coreBaseSettings;
}
public IAsyncEnumerable<Tuple<FileEntry<T>, bool>> CanReadAsync<T>(IAsyncEnumerable<FileEntry<T>> entries, Guid userId)
@ -527,6 +530,21 @@ public class FileSecurity : IFileSecurity
private async Task<bool> FilterEntry<T>(FileEntry<T> e, FilesSecurityActions action, Guid userId, IEnumerable<FileShareRecord> shares, bool isOutsider, bool isUser, bool isAuthenticated, bool isDocSpaceAdmin)
{
if (!_coreBaseSettings.DisableDocSpace)
{
if (
e.RootFolderType == FolderType.COMMON ||
e.RootFolderType == FolderType.SHARE ||
e.RootFolderType == FolderType.Recent ||
e.RootFolderType == FolderType.Favorites ||
e.RootFolderType == FolderType.Templates ||
e.RootFolderType == FolderType.Privacy ||
e.RootFolderType == FolderType.Projects)
{
return false;
}
}
if (e.RootFolderType == FolderType.COMMON ||
e.RootFolderType == FolderType.USER ||
e.RootFolderType == FolderType.SHARE ||
@ -995,23 +1013,28 @@ public class FileSecurity : IFileSecurity
}
public async Task<List<FileEntry>> GetVirtualRoomsAsync(FilterType filterType, Guid subjectId, string searchText, bool searchInContent, bool withSubfolders,
SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider)
SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter)
{
var securityDao = _daoFactory.GetSecurityDao<int>();
var subjectEntries = subjectFilter is SubjectFilter.Member
? await securityDao.GetSharesAsync(new[] { subjectId }).Where(r => r.EntryType == FileEntryType.Folder).Select(r => r.EntryId.ToString()).ToListAsync()
: null;
if (_fileSecurityCommon.IsDocSpaceAdministrator(_authContext.CurrentAccount.ID))
{
return await GetVirtualRoomsForDocSpaceAdminAsync(filterType, subjectId, searchText, searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider);
return await GetVirtualRoomsForDocSpaceAdminAsync(filterType, subjectId, searchText, searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider, subjectFilter, subjectEntries);
}
var securityDao = _daoFactory.GetSecurityDao<int>();
var subjects = GetUserSubjects(_authContext.CurrentAccount.ID);
var records = await securityDao.GetSharesAsync(subjects).ToListAsync();
var currentUserSubjects = GetUserSubjects(_authContext.CurrentAccount.ID);
var currentUsersRecords = await securityDao.GetSharesAsync(currentUserSubjects).ToListAsync();
var thirdpartyIds = await GetThirdpartyRoomsIdsAsync(searchArea);
var entries = new List<FileEntry>();
var rooms = await GetVirtualRoomsForUserAsync(records.Where(r => r.EntryId is int), Array.Empty<int>(), subjects, filterType, subjectId, searchText, searchInContent,
withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider);
var thirdPartyRooms = await GetVirtualRoomsForUserAsync(records.Where(r => r.EntryId is string), thirdpartyIds, subjects, filterType, subjectId, searchText,
searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider);
var rooms = await GetVirtualRoomsForUserAsync(currentUsersRecords.Where(r => r.EntryId is int), Array.Empty<int>(), currentUserSubjects, filterType, subjectId, searchText, searchInContent,
withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider, subjectFilter, subjectEntries);
var thirdPartyRooms = await GetVirtualRoomsForUserAsync(currentUsersRecords.Where(r => r.EntryId is string), thirdpartyIds, currentUserSubjects, filterType, subjectId, searchText,
searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider, subjectFilter, subjectEntries);
entries.AddRange(rooms);
entries.AddRange(thirdPartyRooms);
@ -1047,7 +1070,7 @@ public class FileSecurity : IFileSecurity
}
private async Task<List<FileEntry>> GetVirtualRoomsForDocSpaceAdminAsync(FilterType filterType, Guid subjectId, string search, bool searchInContent, bool withSubfolders,
SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider)
SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntries)
{
var folderDao = _daoFactory.GetFolderDao<int>();
var folderThirdPartyDao = _daoFactory.GetFolderDao<string>();
@ -1064,8 +1087,8 @@ public class FileSecurity : IFileSecurity
var roomsFolderId = await _globalFolder.GetFolderVirtualRoomsAsync<int>(_daoFactory);
var thirdPartyRoomsIds = await providerDao.GetProvidersInfoAsync(FolderType.VirtualRooms).Select(p => p.FolderId).ToListAsync();
var roomsEntries = await folderDao.GetRoomsAsync(roomsFolderId, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider).ToListAsync();
var thirdPartyRoomsEntries = await folderThirdPartyDao.GetRoomsAsync(Array.Empty<string>(), thirdPartyRoomsIds, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider)
var roomsEntries = await folderDao.GetRoomsAsync(roomsFolderId, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntries).ToListAsync();
var thirdPartyRoomsEntries = await folderThirdPartyDao.GetRoomsAsync(Array.Empty<string>(), thirdPartyRoomsIds, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntries)
.ToListAsync();
foldersInt.AddRange(roomsEntries);
@ -1096,8 +1119,8 @@ public class FileSecurity : IFileSecurity
var archiveFolderId = await _globalFolder.GetFolderArchiveAsync<int>(_daoFactory);
var thirdPartyRoomsIds = await providerDao.GetProvidersInfoAsync(FolderType.Archive).Select(p => p.FolderId).ToListAsync();
var roomsEntries = await folderDao.GetRoomsAsync(archiveFolderId, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider).ToListAsync();
var thirdPartyRoomsEntries = await folderThirdPartyDao.GetRoomsAsync(Array.Empty<string>(), thirdPartyRoomsIds, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider)
var roomsEntries = await folderDao.GetRoomsAsync(archiveFolderId, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntries).ToListAsync();
var thirdPartyRoomsEntries = await folderThirdPartyDao.GetRoomsAsync(Array.Empty<string>(), thirdPartyRoomsIds, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntries)
.ToListAsync();
foldersInt.AddRange(roomsEntries);
@ -1136,7 +1159,7 @@ public class FileSecurity : IFileSecurity
}
private async Task<List<FileEntry>> GetVirtualRoomsForUserAsync<T>(IEnumerable<FileShareRecord> records, IEnumerable<T> proivdersIds, List<Guid> subjects, FilterType filterType, Guid subjectId, string search,
bool searchInContent, bool withSubfolders, SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider)
bool searchInContent, bool withSubfolders, SearchArea searchArea, bool withoutTags, IEnumerable<string> tagNames, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntries)
{
var folderDao = _daoFactory.GetFolderDao<T>();
var fileDao = _daoFactory.GetFileDao<T>();
@ -1193,7 +1216,7 @@ public class FileSecurity : IFileSecurity
return false;
};
var fileEntries = await folderDao.GetRoomsAsync(rootFoldersIds, roomsIds.Keys, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider)
var fileEntries = await folderDao.GetRoomsAsync(rootFoldersIds, roomsIds.Keys, filterType, tagNames, subjectId, search, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntries)
.Where(filter).ToListAsync();
await SetTagsAsync(fileEntries);

View File

@ -0,0 +1,33 @@
// (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.Files.Core.Core;
public enum SubjectFilter
{
Owner = 0,
Member = 1,
}

View File

@ -75,7 +75,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders,
bool withoutTags, bool excludeSubject, ProviderFilter provider)
bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.Box))
{
@ -85,7 +85,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -98,7 +98,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText,
bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.Box))
{
@ -108,7 +108,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -77,7 +77,7 @@ internal class DropboxFolderDao : DropboxDaoBase, IFolderDao<string>
return GetRootFolderAsync(fileId);
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.DropBox))
{
@ -87,7 +87,7 @@ internal class DropboxFolderDao : DropboxDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -99,7 +99,7 @@ internal class DropboxFolderDao : DropboxDaoBase, IFolderDao<string>
return rooms;
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.DropBox))
{
@ -109,7 +109,7 @@ internal class DropboxFolderDao : DropboxDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -74,7 +74,7 @@ internal class GoogleDriveFolderDao : GoogleDriveDaoBase, IFolderDao<string>
return GetRootFolderAsync("");
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.GoogleDrive))
{
@ -84,7 +84,7 @@ internal class GoogleDriveFolderDao : GoogleDriveDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -96,7 +96,8 @@ internal class GoogleDriveFolderDao : GoogleDriveDaoBase, IFolderDao<string>
return rooms;
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter,
IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.GoogleDrive))
{
@ -106,7 +107,7 @@ internal class GoogleDriveFolderDao : GoogleDriveDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -456,12 +456,20 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
return rooms.Where(f => f != null && (f.FolderType == filter || filter == FolderType.DEFAULT));
}
protected IAsyncEnumerable<Folder<string>> FilterBySubject(IAsyncEnumerable<Folder<string>> rooms, Guid subjectId, bool excludeSubject)
protected IAsyncEnumerable<Folder<string>> FilterBySubject(IAsyncEnumerable<Folder<string>> rooms, Guid subjectId, bool excludeSubject, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds = null)
{
if (subjectId != Guid.Empty)
{
if (subjectFilter == SubjectFilter.Owner)
{
rooms = excludeSubject ? rooms.Where(f => f != null && f.CreateBy != subjectId) : rooms.Where(f => f != null && f.CreateBy == subjectId);
}
else if (subjectFilter == SubjectFilter.Member)
{
rooms = excludeSubject ? rooms.Where(f => f != null && f.CreateBy != subjectId && !subjectEntriesIds.Contains(f.Id))
: rooms.Where(f => f != null && (f.CreateBy == subjectId || subjectEntriesIds.Contains(f.Id)));
}
}
return rooms;
}

View File

@ -74,7 +74,7 @@ internal class OneDriveFolderDao : OneDriveDaoBase, IFolderDao<string>
return GetRootFolderAsync(fileId);
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.OneDrive))
{
@ -84,7 +84,7 @@ internal class OneDriveFolderDao : OneDriveDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -96,7 +96,7 @@ internal class OneDriveFolderDao : OneDriveDaoBase, IFolderDao<string>
return rooms;
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.OneDrive))
{
@ -106,7 +106,7 @@ internal class OneDriveFolderDao : OneDriveDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -90,11 +90,11 @@ internal class ProviderFolderDao : ProviderDaoBase, IFolderDao<string>
return folderDao.GetRootFolderByFileAsync(selector.ConvertId(fileId));
}
public async IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public async IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
var selector = GetSelector(parentId);
var folderDao = selector.GetFolderDao(parentId);
var rooms = folderDao.GetRoomsAsync(selector.ConvertId(parentId), filterType, tags, subjectId, searchText, withSubfolders, withoutTags, excludeSubject, provider);
var rooms = folderDao.GetRoomsAsync(selector.ConvertId(parentId), filterType, tags, subjectId, searchText, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntriesIds);
var result = await FilterByProvider(rooms.Where(r => r != null), provider).ToListAsync();
@ -106,7 +106,7 @@ internal class ProviderFolderDao : ProviderDaoBase, IFolderDao<string>
}
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
var result = AsyncEnumerable.Empty<Folder<string>>();
@ -126,7 +126,7 @@ internal class ProviderFolderDao : ProviderDaoBase, IFolderDao<string>
{
var folderDao = selectorLocal.GetFolderDao(matchedId.FirstOrDefault());
return folderDao.GetRoomsAsync(parentsIds, matchedId.Select(selectorLocal.ConvertId).ToList(), filterType, tags, subjectId, searchText, withSubfolders, withoutTags, excludeSubject, provider);
return folderDao.GetRoomsAsync(parentsIds, matchedId.Select(selectorLocal.ConvertId).ToList(), filterType, tags, subjectId, searchText, withSubfolders, withoutTags, excludeSubject, provider, subjectFilter, subjectEntriesIds);
})
.Where(r => r != null));
}

View File

@ -80,7 +80,7 @@ internal class SharePointFolderDao : SharePointDaoBase, IFolderDao<string>
return Task.FromResult(ProviderInfo.ToFolder(ProviderInfo.RootFolder));
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.SharePoint))
{
@ -90,7 +90,7 @@ internal class SharePointFolderDao : SharePointDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -102,7 +102,7 @@ internal class SharePointFolderDao : SharePointDaoBase, IFolderDao<string>
return rooms;
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.SharePoint))
{
@ -112,7 +112,7 @@ internal class SharePointFolderDao : SharePointDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -79,7 +79,7 @@ internal class SharpBoxFolderDao : SharpBoxDaoBase, IFolderDao<string>
return Task.FromResult(ToFolder(RootFolder()));
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(string parentId, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider, SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.kDrive && provider != ProviderFilter.WebDav && provider != ProviderFilter.Yandex))
{
@ -89,7 +89,7 @@ internal class SharpBoxFolderDao : SharpBoxDaoBase, IFolderDao<string>
var rooms = GetFoldersAsync(parentId);
rooms = FilterByRoomType(rooms, filterType);
rooms = FilterBySubject(rooms, subjectId, excludeSubject);
rooms = FilterBySubject(rooms, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{
@ -101,7 +101,8 @@ internal class SharpBoxFolderDao : SharpBoxDaoBase, IFolderDao<string>
return rooms;
}
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider)
public IAsyncEnumerable<Folder<string>> GetRoomsAsync(IEnumerable<string> parentsIds, IEnumerable<string> roomsIds, FilterType filterType, IEnumerable<string> tags, Guid subjectId, string searchText, bool withSubfolders, bool withoutTags, bool excludeSubject, ProviderFilter provider,
SubjectFilter subjectFilter, IEnumerable<string> subjectEntriesIds)
{
if (CheckInvalidFilter(filterType) || (provider != ProviderFilter.None && provider != ProviderFilter.kDrive && provider != ProviderFilter.WebDav && provider != ProviderFilter.Yandex))
{
@ -111,7 +112,7 @@ internal class SharpBoxFolderDao : SharpBoxDaoBase, IFolderDao<string>
var folders = roomsIds.ToAsyncEnumerable().SelectAwait(async e => await GetFolderAsync(e).ConfigureAwait(false));
folders = FilterByRoomType(folders, filterType);
folders = FilterBySubject(folders, subjectId, excludeSubject);
folders = FilterBySubject(folders, subjectId, excludeSubject, subjectFilter, subjectEntriesIds);
if (!string.IsNullOrEmpty(searchText))
{

View File

@ -29,13 +29,6 @@ namespace ASC.Files.Core.Mapping;
[Scope]
public class FolderMappingAction : IMappingAction<DbFolderQuery, Folder<int>>
{
private readonly CoreBaseSettings _coreBaseSettings;
public FolderMappingAction(CoreBaseSettings coreBaseSettings)
{
_coreBaseSettings = coreBaseSettings;
}
public void Process(DbFolderQuery source, Folder<int> destination, ResolutionContext context)
{
switch (destination.FolderType)
@ -44,7 +37,7 @@ public class FolderMappingAction : IMappingAction<DbFolderQuery, Folder<int>>
destination.Title = FilesUCResource.CorporateFiles;
break;
case FolderType.USER:
destination.Title = _coreBaseSettings.DisableDocSpace ? FilesUCResource.MyFiles : FilesUCResource.Personal;
destination.Title = FilesUCResource.MyFiles;
break;
case FolderType.SHARE:
destination.Title = FilesUCResource.SharedForMe;

View File

@ -61,7 +61,7 @@ namespace ASC.Files.Core.Resources {
}
/// <summary>
/// Looks up a localized string similar to Archived.
/// Looks up a localized string similar to My archive.
/// </summary>
public static string Archive {
get {
@ -169,7 +169,7 @@ namespace ASC.Files.Core.Resources {
}
/// <summary>
/// Looks up a localized string similar to Shared.
/// Looks up a localized string similar to My rooms.
/// </summary>
public static string VirtualRooms {
get {

View File

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Archive" xml:space="preserve">
<value>Archived</value>
<value>My archive</value>
</data>
<data name="CorporateFiles" xml:space="preserve">
<value>Common</value>
@ -154,6 +154,6 @@
<value>Trash</value>
</data>
<data name="VirtualRooms" xml:space="preserve">
<value>Shared</value>
<value>My rooms</value>
</data>
</root>

View File

@ -388,7 +388,7 @@ public class EntryManager
public async Task<(IEnumerable<FileEntry> Entries, int Total)> GetEntriesAsync<T>(Folder<T> parent, int from, int count, FilterType filterType, bool subjectGroup, Guid subjectId,
string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy, SearchArea searchArea = SearchArea.Active, bool withoutTags = false, IEnumerable<string> tagNames = null,
bool excludeSubject = false, ProviderFilter provider = ProviderFilter.None)
bool excludeSubject = false, ProviderFilter provider = ProviderFilter.None, SubjectFilter subjectFilter = SubjectFilter.Owner)
{
var total = 0;
@ -471,7 +471,7 @@ public class EntryManager
}
else if ((parent.FolderType == FolderType.VirtualRooms || parent.FolderType == FolderType.Archive) && !parent.ProviderEntry)
{
entries = await _fileSecurity.GetVirtualRoomsAsync(filterType, subjectId, searchText, searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider);
entries = await _fileSecurity.GetVirtualRoomsAsync(filterType, subjectId, searchText, searchInContent, withSubfolders, searchArea, withoutTags, tagNames, excludeSubject, provider, subjectFilter);
CalculateTotal();
}

View File

@ -653,7 +653,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
/// </returns>
[HttpGet("rooms")]
public async Task<FolderContentDto<int>> GetRoomsFolderAsync(RoomFilterType? type, string subjectId, bool? searchInContent, bool? withSubfolders, SearchArea? searchArea, bool? withoutTags, string tags, bool? excludeSubject,
ProviderFilter? provider)
ProviderFilter? provider, SubjectFilter? subjectFilter)
{
ErrorIfNotDocSpace();
@ -685,7 +685,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
var content = await _fileStorageServiceInt.GetFolderItemsAsync(parentId, startIndex, count, filter, false, subjectId, filterValue,
searchInContent ?? false, withSubfolders ?? false, orderBy, searchArea ?? SearchArea.Active, withoutTags ?? false, tagNames, excludeSubject ?? false,
provider ?? ProviderFilter.None);
provider ?? ProviderFilter.None, subjectFilter ?? SubjectFilter.Owner);
var dto = await _folderContentDtoHelper.GetAsync(content, startIndex);

View File

@ -28,7 +28,6 @@ namespace ASC.Files.Helpers;
public class FoldersControllerHelper<T> : FilesHelperBase<T>
{
private readonly EntryManager _entryManager;
private readonly UserManager _userManager;
private readonly SecurityContext _securityContext;
private readonly GlobalFolderHelper _globalFolderHelper;
@ -45,7 +44,6 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
FolderContentDtoHelper folderContentDtoHelper,
IHttpContextAccessor httpContextAccessor,
FolderDtoHelper folderDtoHelper,
EntryManager entryManager,
UserManager userManager,
SecurityContext securityContext,
GlobalFolderHelper globalFolderHelper,
@ -66,7 +64,6 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
_coreBaseSettings = coreBaseSettings;
_fileUtility = fileUtility;
_securityContext = securityContext;
_entryManager = entryManager;
_userManager = userManager;
}
@ -108,8 +105,10 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
yield return _globalFolderHelper.FolderMy;
}
if (!_coreBaseSettings.Personal && _coreBaseSettings.DisableDocSpace
&& !_userManager.IsOutsider(user))
if (_coreBaseSettings.DisableDocSpace)
{
if (!_coreBaseSettings.Personal &&
!_userManager.IsOutsider(user))
{
yield return await _globalFolderHelper.FolderShareAsync;
}
@ -128,26 +127,25 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
if (!IsUser &&
!_coreBaseSettings.Personal &&
_coreBaseSettings.DisableDocSpace &&
PrivacyRoomSettings.IsAvailable())
{
yield return await _globalFolderHelper.FolderPrivacyAsync;
}
}
if (!_coreBaseSettings.Personal && _coreBaseSettings.DisableDocSpace)
if (!_coreBaseSettings.Personal)
{
yield return await _globalFolderHelper.FolderCommonAsync;
}
if (!IsUser
&& _coreBaseSettings.DisableDocSpace
&& !withoutAdditionalFolder
&& _fileUtility.ExtsWebTemplate.Count > 0
&& _filesSettingsHelper.TemplatesSection)
if (!IsUser &&
!withoutAdditionalFolder &&
_fileUtility.ExtsWebTemplate.Count > 0 &&
_filesSettingsHelper.TemplatesSection)
{
yield return await _globalFolderHelper.FolderTemplatesAsync;
}
}
if (!withoutTrash && !IsUser)
{

View File

@ -555,13 +555,13 @@ public class UserController : PeopleControllerBase
_apiContext.SetDataFiltered();
}
return GetFullByFilter(status, groupId, null, null, null);
return GetFullByFilter(status, groupId, null, null, null, null);
}
[HttpGet("filter")]
public async IAsyncEnumerable<EmployeeFullDto> GetFullByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator)
public async IAsyncEnumerable<EmployeeFullDto> GetFullByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments)
{
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator);
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments);
foreach (var user in users)
{
@ -607,9 +607,9 @@ public class UserController : PeopleControllerBase
}
[HttpGet("simple/filter")]
public async IAsyncEnumerable<EmployeeDto> GetSimpleByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator)
public async IAsyncEnumerable<EmployeeDto> GetSimpleByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments)
{
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator);
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments);
foreach (var user in users)
{
@ -1274,7 +1274,7 @@ public class UserController : PeopleControllerBase
return string.Empty;
}
private IQueryable<UserInfo> GetByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isDocSpaceAdministrator)
private IQueryable<UserInfo> GetByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isDocSpaceAdministrator, Payments? payments)
{
if (_coreBaseSettings.Personal)
{
@ -1296,8 +1296,12 @@ public class UserController : PeopleControllerBase
{
switch (employeeType)
{
case EmployeeType.DocSpaceAdmin:
includeGroups.Add(new List<Guid> { Constants.GroupAdmin.ID });
break;
case EmployeeType.RoomAdmin:
excludeGroups.Add(Constants.GroupUser.ID);
excludeGroups.Add(Constants.GroupAdmin.ID);
break;
case EmployeeType.User:
includeGroups.Add(new List<Guid> { Constants.GroupUser.ID });
@ -1305,6 +1309,19 @@ public class UserController : PeopleControllerBase
}
}
if (payments != null)
{
switch (payments)
{
case Payments.Paid:
excludeGroups.Add(Constants.GroupUser.ID);
break;
case Payments.Free:
includeGroups.Add(new List<Guid> { Constants.GroupUser.ID });
break;
}
}
if (isDocSpaceAdministrator.HasValue && isDocSpaceAdministrator.Value)
{
var adminGroups = new List<Guid>

View File

@ -194,6 +194,7 @@
"NoTags": "No tags",
"Title": "Title",
"TitleSelectFile": "Select",
"Today": "Today",
"Type": "Type",
"Terabyte": "TB",
"Unknown": "Unknown",
@ -214,6 +215,7 @@
"HelpCenter": "Help center",
"FeedbackAndSupport": "Feedback & Support",
"VideoGuides": "Video Guides",
"Yesterday": "Yesterday",
"SelectFile": "Select file",
"HideArticleMenu": "Hide menu"
}

View File

@ -175,6 +175,7 @@
"NoTags": "Нет тэгов",
"Title": "Заголовок",
"TitleSelectFile": "Выделить",
"Today": "Сегодня",
"Type": "Тип",
"Terabyte": "Тб",
"Unknown": "Неизвестный",
@ -191,6 +192,7 @@
"AboutCompanyTitle": "О программе",
"LogoutButton": "Выйти",
"HelpCenter": "Справочный центр",
"Yesterday": "Вчера",
"SelectFile": "Выбрать файл",
"HideArticleMenu": "Скрыть меню"
}

View File

@ -371,8 +371,7 @@ public class SettingsController : BaseSettingsController
_settingsManager.SaveForCurrentUser(collaboratorPopupSettings);
}
[AllowNotPayment]
[AllowAnonymous]
[AllowAnonymous, AllowNotPayment, AllowSuspended]
[HttpGet("colortheme")]
public CustomColorThemesSettingsDto GetColorTheme()
{

View File

@ -1149,6 +1149,51 @@ namespace ASC.Web.Core.PublicResources {
}
}
/// <summary>
/// Looks up a localized string similar to Telegram.
/// </summary>
public static string ConsumersTelegram {
get {
return ResourceManager.GetString("ConsumersTelegram", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bot username.
/// </summary>
public static string ConsumersTelegramBotName {
get {
return ResourceManager.GetString("ConsumersTelegramBotName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bot token.
/// </summary>
public static string ConsumersTelegramBotToken {
get {
return ResourceManager.GetString("ConsumersTelegramBotToken", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connect Telegram account to receive portal notifications via Telegram..
/// </summary>
public static string ConsumersTelegramDescription {
get {
return ResourceManager.GetString("ConsumersTelegramDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to To receive portal notifications via Telegram, enable Telegram bot. You can create a new bot using BotFather in Telegram Desktop. To use this bot, portal users need to enable Telegram notifications on their Profile Page. {0}Paste bots username and the token you received in the fields below..
/// </summary>
public static string ConsumersTelegramInstructionV11 {
get {
return ResourceManager.GetString("ConsumersTelegramInstructionV11", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Twilio.
/// </summary>

View File

@ -909,4 +909,19 @@
<data name="ErrorIpSecurity" xml:space="preserve">
<value>The access to the portal is restricted</value>
</data>
<data name="ConsumersTelegram" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="ConsumersTelegramBotName" xml:space="preserve">
<value>Bot username</value>
</data>
<data name="ConsumersTelegramBotToken" xml:space="preserve">
<value>Bot token</value>
</data>
<data name="ConsumersTelegramDescription" xml:space="preserve">
<value>Connect Telegram account to receive portal notifications via Telegram.</value>
</data>
<data name="ConsumersTelegramInstructionV11" xml:space="preserve">
<value>To receive portal notifications via Telegram, enable Telegram bot. You can create a new bot using BotFather in Telegram Desktop. To use this bot, portal users need to enable Telegram notifications on their Profile Page. {0}Paste bots username and the token you received in the fields below.</value>
</data>
</root>