Merge branch 'develop' into feature/sso-mobile-layout

This commit is contained in:
Viktor Fomin 2023-09-07 12:57:59 +03:00
commit 2cc96cdb2c
22 changed files with 527 additions and 293 deletions

View File

@ -61,16 +61,7 @@ public class SocketServiceClient
}
try
{
var request = new HttpRequestMessage();
request.Headers.Add("Authorization", CreateAuthToken());
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(GetMethod(method));
var jsonData = JsonConvert.SerializeObject(data);
_logger.DebugMakeRequest(method, jsonData);
request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var request = GenerateRequest(method, data);
var httpClient = _clientFactory.CreateClient();
//async
@ -100,11 +91,33 @@ public class SocketServiceClient
return JsonConvert.DeserializeObject<T>(resultMakeRequest);
}
protected void SendNotAwaitableRequest(string method, object data)
{
var request = GenerateRequest(method, data);
var httpClient = _clientFactory.CreateClient();
_ = httpClient.SendAsync(request);
}
private bool IsAvailable()
{
return _enableSocket && _lastErrorTime + _timeout < DateTime.Now;
}
private HttpRequestMessage GenerateRequest(string method, object data)
{
var request = new HttpRequestMessage();
request.Headers.Add("Authorization", CreateAuthToken());
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(GetMethod(method));
var jsonData = JsonConvert.SerializeObject(data);
_logger.DebugMakeRequest(method, jsonData);
request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
return request;
}
private string GetMethod(string method)
{

View File

@ -42,12 +42,12 @@
});
router.post("/markasnew-file", (req, res) => {
files.markAsNewFile(req.body);
files.markAsNewFiles(req.body);
res.end();
});
router.post("/markasnew-folder", (req, res) => {
files.markAsNewFolder(req.body);
files.markAsNewFolders(req.body);
res.end();
});
@ -62,4 +62,4 @@
});
return router;
};
};

View File

@ -171,11 +171,19 @@
filesIO.to(room).emit("s:markasnew-file", { fileId, count });
}
function markAsNewFiles(items = []) {
items.forEach(markAsNewFile);
}
function markAsNewFolder({ folderId, count, room } = {}) {
logger.info(`markAsNewFolder ${folderId} in room ${room}:${count}`);
filesIO.to(room).emit("s:markasnew-folder", { folderId, count });
}
function markAsNewFolders(items = []) {
items.forEach(markAsNewFolder);
}
function changeQuotaUsedValue({ featureId, value, room } = {}) {
logger.info(`changeQuotaUsedValue in room ${room}`, { featureId, value });
filesIO.to(room).emit("s:change-quota-used-value", { featureId, value });
@ -195,9 +203,9 @@
deleteFolder,
updateFile,
updateFolder,
markAsNewFile,
markAsNewFolder,
changeQuotaUsedValue,
changeQuotaFeatureValue,
markAsNewFiles,
markAsNewFolders
};
};

View File

@ -126,12 +126,14 @@ const InfoPanelHeaderContent = (props) => {
style={{ width: "100%" }}
data={roomsSubmenu}
forsedActiveItemId={roomsView}
scale={true}
/>
) : (
<Submenu
style={{ width: "100%" }}
data={personalSubmenu}
forsedActiveItemId={fileView}
scale={true}
/>
)}
</div>

View File

@ -5,6 +5,7 @@ const StyledSettingsSeparator = styled.hr`
margin: 24px 0;
border: none;
border-top: ${(props) => props.theme.client.settings.separatorBorder};
max-width: 700px;
`;
StyledSettingsSeparator.defaultProps = { theme: Base };

View File

@ -24,13 +24,13 @@ const StyledComponent = styled.div`
.category-description {
margin-top: 5px;
line-height: 20px;
color: ${props => props.theme.client.settings.common.descriptionColor};
color: ${(props) => props.theme.client.settings.common.descriptionColor};
margin-bottom: 20px;
max-width: 700px;
}
.category-item-description {
color: ${props => props.theme.client.settings.common.descriptionColor};
color: ${(props) => props.theme.client.settings.common.descriptionColor};
font-size: 12px;
max-width: 1024px;
}
@ -45,7 +45,7 @@ const StyledComponent = styled.div`
font-weight: bold;
font-size: 16px;
line-height: 22px;
${props =>
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-left: 4px;
@ -69,7 +69,7 @@ const StyledComponent = styled.div`
StyledComponent.defaultProps = { theme: Base };
const Customization = props => {
const Customization = (props) => {
const {
t,
isLoaded,

View File

@ -18,7 +18,7 @@ import config from "../../../../../package.json";
import ManualBackup from "./backup/manual-backup";
import AutoBackup from "./backup/auto-backup";
const DataManagementWrapper = props => {
const DataManagementWrapper = (props) => {
const {
dataBackupUrl,
automaticBackupUrl,
@ -68,7 +68,8 @@ const DataManagementWrapper = props => {
href={isAutoBackupPage ? automaticBackupUrl : dataBackupUrl}
target="_blank"
isBold
isHovered>
isHovered
>
{t("Common:LearnMore")}
</Link>
</Box>
@ -99,13 +100,13 @@ const DataManagementWrapper = props => {
useEffect(() => {
const path = location.pathname;
const currentTab = data.findIndex(item => path.includes(item.id));
const currentTab = data.findIndex((item) => path.includes(item.id));
if (currentTab !== -1) setCurrentTab(currentTab);
setIsLoading(true);
}, []);
const onSelect = e => {
const onSelect = (e) => {
navigate(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
@ -120,7 +121,11 @@ const DataManagementWrapper = props => {
return isNotPaidPeriod ? (
<ManualBackup buttonSize={buttonSize} renderTooltip={renderTooltip} />
) : (
<Submenu data={data} startSelect={currentTab} onSelect={e => onSelect(e)} />
<Submenu
data={data}
startSelect={currentTab}
onSelect={(e) => onSelect(e)}
/>
);
};

View File

@ -61,6 +61,10 @@ const MainContainer = styled.div`
position: absolute;
z-index: 1 !important;
}
.history-row-container {
max-width: 700px;
}
}
${(props) => props.isSettingNotPaid && UnavailableStyles}

View File

@ -21,6 +21,7 @@ const Submenu = (props) => {
startSelect = 0,
forsedActiveItemId,
onSelect,
scale,
...rest
} = props;
if (!data) return null;
@ -105,7 +106,7 @@ const Submenu = (props) => {
<div className="sticky">
<SubmenuRoot>
<SubmenuScrollbarSize />
<SubmenuScroller>
<SubmenuScroller scale={scale}>
<StyledSubmenuItems ref={submenuItemsRef} role="list">
{data.map((d) => {
const isActive =
@ -144,9 +145,10 @@ const Submenu = (props) => {
);
})}
</StyledSubmenuItems>
{!scale && <StyledSubmenuBottomLine className="bottom-line" />}
</SubmenuScroller>
</SubmenuRoot>
<StyledSubmenuBottomLine className="bottom-line" />
{scale && <StyledSubmenuBottomLine className="bottom-line" />}
</div>
<div className="sticky-indent"></div>
@ -164,6 +166,8 @@ Submenu.propTypes = {
startSelect: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
/** Property that allows explicitly selecting content passed through an external operation */
forsedActiveItemId: PropTypes.any,
/** Scales the width of the bottom line to 100%. */
scale: PropTypes.bool,
/** Sets a callback function that is triggered when the submenu item is selected */
onSelect: PropTypes.func,
};

View File

@ -83,7 +83,9 @@ export const StyledSubmenuItem = styled.div.attrs((props) => ({
margin-left: 17px;
`
: css`
margin-right: 17px;
&:not(:last-child) {
margin-right: 17px;
}
`}
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
`;
@ -106,7 +108,6 @@ StyledSubmenuItemText.defaultProps = { theme: Base };
export const StyledSubmenuItemLabel = styled.div`
z-index: 1;
width: 100%;
//margin-left: -14px;
height: 4px;
bottom: 0px;
border-radius: 4px 4px 0 0;
@ -127,6 +128,13 @@ export const SubmenuScroller = styled.div`
}
overflow-x: auto;
overflow-y: hidden;
${(props) =>
!props.scale &&
css`
display: grid;
flex: 0 1 auto;
`}
`;
export const SubmenuRoot = styled.div`

View File

@ -46,6 +46,7 @@ public interface ITagDao<T>
Task<TagInfo> SaveTagInfoAsync(TagInfo tagInfo);
Task UpdateNewTags(IEnumerable<Tag> tag, Guid createdBy = default);
Task UpdateNewTags(Tag tag);
Task IncrementNewTagsAsync(IEnumerable<Tag> tags, Guid createdBy = default);
Task RemoveTagsAsync(IEnumerable<int> tagsIds);
Task RemoveTagsAsync(FileEntry<T> entry, IEnumerable<int> tagsIds);
Task RemoveTags(IEnumerable<Tag> tag);

View File

@ -276,15 +276,14 @@ internal abstract class SecurityBaseDao<T> : AbstractDao
return result;
}
internal async Task<FileShareRecord> ToFileShareRecordAsync(SecurityTreeRecord r)
protected FileShareRecord ToFileShareRecord(SecurityTreeRecord r)
{
var result = await ToFileShareRecordAsync(r.DbFilesSecurity);
if (r.DbFolderTree != null)
{
result.EntryId = r.DbFolderTree.FolderId;
}
var result = _mapper.Map<SecurityTreeRecord, FileShareRecord>(r);
result.Level = r.DbFolderTree?.Level ?? -1;
if (r.FolderId != default)
{
result.EntryId = r.FolderId;
}
return result;
}
@ -309,7 +308,7 @@ internal class SecurityDao : SecurityBaseDao<int>, ISecurityDao<int>
{
}
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<int> entry)
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<int> entry, IEnumerable<Guid> subjects = null)
{
if (entry == null)
{
@ -321,30 +320,56 @@ internal class SecurityDao : SecurityBaseDao<int>, ISecurityDao<int>
await SelectFilesAndFoldersForShareAsync(entry, files, null, foldersInt);
return await SaveFilesAndFoldersForShareAsync(files, foldersInt);
}
private async Task<IEnumerable<FileShareRecord>> SaveFilesAndFoldersForShareAsync(List<string> files, List<int> folders)
{
await using var filesDbContext = _dbContextFactory.CreateDbContext();
var q = await Query(filesDbContext.Security)
.Join(filesDbContext.Tree, r => r.EntryId, a => a.ParentId.ToString(), (security, tree) => new SecurityTreeRecord { DbFilesSecurity = security, DbFolderTree = tree })
.Where(r => folders.Contains(r.DbFolderTree.FolderId) &&
r.DbFilesSecurity.EntryType == FileEntryType.Folder)
.ToListAsync();
var q = Query(filesDbContext.Security)
.Join(filesDbContext.Tree, r => r.EntryId, a => a.ParentId.ToString(),
(s, t) => new SecurityTreeRecord
{
TenantId = s.TenantId,
EntryId = s.EntryId,
EntryType = s.EntryType,
SubjectType = s.SubjectType,
Subject = s.Subject,
Owner = s.Owner,
Share = s.Share,
TimeStamp = s.TimeStamp,
Options = s.Options,
FolderId = t.FolderId,
ParentId = t.ParentId,
Level = t.Level
})
.Where(r => foldersInt.Contains(r.FolderId) && r.EntryType == FileEntryType.Folder);
if (0 < files.Count)
if (files.Count > 0)
{
var q1 = await GetQuery(filesDbContext, r => files.Contains(r.EntryId) && r.EntryType == FileEntryType.File)
.Select(r => new SecurityTreeRecord { DbFilesSecurity = r })
.ToListAsync();
q = q.Union(q1).ToList();
var q1 = GetQuery(filesDbContext, r => files.Contains(r.EntryId) && r.EntryType == FileEntryType.File)
.Select(s => new SecurityTreeRecord
{
TenantId = s.TenantId,
EntryId = s.EntryId,
EntryType = s.EntryType,
SubjectType = s.SubjectType,
Subject = s.Subject,
Owner = s.Owner,
Share = s.Share,
TimeStamp = s.TimeStamp,
Options = s.Options,
FolderId = 0,
ParentId = 0,
Level = -1
});
q = q.Concat(q1);
}
var records = await q
.ToAsyncEnumerable()
.SelectAwait(async e => await ToFileShareRecordAsync(e))
if (subjects != null && subjects.Any())
{
q = q.Where(r => subjects.Contains(r.Subject));
}
var records = await q.ToAsyncEnumerable()
.Select(ToFileShareRecord)
.OrderBy(r => r.Level)
.ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer())
.ToListAsync();
@ -404,7 +429,7 @@ internal class ThirdPartySecurityDao : SecurityBaseDao<string>, ISecurityDao<str
_selectorFactory = selectorFactory;
}
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<string> entry)
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<string> entry, IEnumerable<Guid> subjects = null)
{
var result = new List<FileShareRecord>();
@ -433,6 +458,11 @@ internal class ThirdPartySecurityDao : SecurityBaseDao<string>, ISecurityDao<str
result.AddRange(await GetShareForFoldersAsync(folders).ToListAsync());
if (subjects != null && subjects.Any())
{
result = result.Where(r => subjects.Contains(r.Subject)).ToList();
}
return result;
}
@ -493,12 +523,21 @@ internal class ThirdPartySecurityDao : SecurityBaseDao<string>, ISecurityDao<str
}
}
}
}
}
internal class SecurityTreeRecord
{
public DbFilesSecurity DbFilesSecurity { get; init; }
public DbFolderTree DbFolderTree { get; init; }
public int TenantId { get; set; }
public string EntryId { get; set; }
public FileEntryType EntryType { get; set; }
public SubjectType SubjectType { get; set; }
public Guid Subject { get; set; }
public Guid Owner { get; set; }
public FileShare Share { get; set; }
public DateTime TimeStamp { get; set; }
public string Options { get; set; }
public int FolderId { get; set; }
public int ParentId { get; set; }
public int Level { get; set; }
}
static file class Queries

View File

@ -280,30 +280,81 @@ internal abstract class BaseTagDao<T> : AbstractDao
return result;
}
await _semaphore.WaitAsync();
await using var filesDbContext = _dbContextFactory.CreateDbContext();
var strategy = filesDbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
try
{
await _semaphore.WaitAsync();
await using var filesDbContext = _dbContextFactory.CreateDbContext();
await using var tx = await filesDbContext.Database.BeginTransactionAsync();
await DeleteTagsBeforeSave();
var strategy = filesDbContext.Database.CreateExecutionStrategy();
var createOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow());
var cacheTagId = new Dictionary<string, int>();
foreach (var t in tags)
await strategy.ExecuteAsync(async () =>
{
result.Add(await SaveTagAsync(t, cacheTagId, createOn, createdBy));
}
await using var internalFilesDbContext = _dbContextFactory.CreateDbContext();
await using var tx = await internalFilesDbContext.Database.BeginTransactionAsync();
await tx.CommitAsync();
});
await DeleteTagsBeforeSave();
_semaphore.Release();
var createOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow());
var cachedTags = new Dictionary<string, DbFilesTag>();
foreach (var t in tags)
{
var key = GetCacheKey(t);
if (cachedTags.ContainsKey(GetCacheKey(t)))
{
continue;
}
var id = await Queries.TagIdAsync(internalFilesDbContext, t.Owner, t.Name, t.Type);
var toAdd = new DbFilesTag
{
Id = id,
Name = t.Name,
Owner = t.Owner,
Type = t.Type,
TenantId = TenantID
};
if (id == 0)
{
toAdd = internalFilesDbContext.Tag.Add(toAdd).Entity;
}
cachedTags.Add(key, toAdd);
}
await internalFilesDbContext.SaveChangesAsync();
foreach (var t in tags)
{
var key = GetCacheKey(t);
var linkToInsert = new DbFilesTagLink
{
TenantId = TenantID,
TagId = cachedTags.Get(key).Id,
EntryId = (await MappingIDAsync(t.EntryId, true)).ToString(),
EntryType = t.EntryType,
CreateBy = createdBy != default ? createdBy : _authContext.CurrentAccount.ID,
CreateOn = createOn,
Count = t.Count
};
await internalFilesDbContext.AddOrUpdateAsync(r => r.TagLink, linkToInsert);
result.Add(t);
}
await internalFilesDbContext.SaveChangesAsync();
await tx.CommitAsync();
});
}
finally
{
_semaphore.Release();
}
return result;
}
@ -417,6 +468,45 @@ internal abstract class BaseTagDao<T> : AbstractDao
return t;
}
public async Task IncrementNewTagsAsync(IEnumerable<Tag> tags, Guid createdBy = default)
{
if (tags == null || !tags.Any())
{
return;
}
try
{
await _semaphore.WaitAsync();
await using var filesDbContext = _dbContextFactory.CreateDbContext();
var strategy = filesDbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var internalFilesDbContext = _dbContextFactory.CreateDbContext();
await using var tx = await internalFilesDbContext.Database.BeginTransactionAsync();
var createOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow());
var tenantId = TenantID;
foreach (var tagsGroup in tags.GroupBy(t => new { t.EntryId, t.EntryType }))
{
var mappedId = (await MappingIDAsync(tagsGroup.Key.EntryId)).ToString();
await Queries.IncrementNewTagsAsync(filesDbContext, tenantId, tagsGroup.Select(t => t.Id), tagsGroup.Key.EntryType,
mappedId, createdBy, createOn);
}
await tx.CommitAsync();
});
}
finally
{
_semaphore.Release();
}
}
public async Task UpdateNewTags(IEnumerable<Tag> tags, Guid createdBy = default)
{
if (tags == null || !tags.Any())
@ -662,6 +752,11 @@ internal abstract class BaseTagDao<T> : AbstractDao
return result;
}
private string GetCacheKey(Tag tag)
{
return string.Join("/", TenantID.ToString(), tag.Owner.ToString(), tag.Name, ((int)tag.Type).ToString(CultureInfo.InvariantCulture));
}
}
@ -1267,4 +1362,18 @@ static file class Queries
.Where(r => r.TenantId == tenantId)
.Where(r => tagsIds.Contains(r.Id))
.ExecuteDelete());
public static readonly Func<FilesDbContext, int, IEnumerable<int>, FileEntryType, string, Guid, DateTime, Task<int>>
IncrementNewTagsAsync = Microsoft.EntityFrameworkCore.EF.CompileAsyncQuery(
(FilesDbContext ctx, int tenantId, IEnumerable<int> tagsIds, FileEntryType tagEntryType, string mappedId, Guid createdBy,
DateTime createOn) =>
ctx.TagLink
.Where(r => r.TenantId == tenantId)
.Where(r => tagsIds.Contains(r.TagId))
.Where(r => r.EntryType == tagEntryType)
.Where(r => r.EntryId == mappedId)
.ExecuteUpdate(f => f
.SetProperty(p => p.CreateBy, createdBy)
.SetProperty(p => p.CreateOn, createOn)
.SetProperty(p => p.Count, p => p.Count + 1)));
}

View File

@ -181,7 +181,7 @@ public abstract class FileEntry<T> : FileEntry, ICloneable, IFileEntry<T>
public override int GetHashCode()
{
return Id.GetHashCode();
return HashCode.Combine(Id, FileEntryType);
}
public override string ToString()

View File

@ -181,7 +181,7 @@ public class FileSecurity : IFileSecurity
private readonly BadgesSettingsHelper _badgesSettingsHelper;
private readonly ExternalShare _externalShare;
private readonly ConcurrentDictionary<string, FileShareRecord> _cachedRecords = new();
private readonly ConcurrentDictionary<string, bool> _cachedFullAccess = new();
private readonly ConcurrentDictionary<string, Guid> _cachedRoomOwner = new();
public FileSecurity(
IDaoFactory daoFactory,
@ -425,7 +425,7 @@ public class FileSecurity : IFileSecurity
private async Task<IEnumerable<Guid>> WhoCanAsync<T>(FileEntry<T> entry, FilesSecurityActions action)
{
var shares = await GetSharesAsync(entry);
var copyShares = shares.ToList();
var copyShares = shares.GroupBy(k => k.Subject).ToDictionary(k => k.Key);
FileShareRecord[] defaultRecords;
@ -582,11 +582,22 @@ public class FileSecurity : IFileSecurity
var result = new List<Guid>();
await foreach (var x in manyShares)
await foreach (var userId in manyShares)
{
if (await CanAsync(entry, x, action, copyShares))
var userSubjects = await GetUserSubjectsAsync(userId);
var userShares = new List<FileShareRecord>();
foreach (var subject in userSubjects)
{
result.Add(x);
if (copyShares.TryGetValue(subject, out var value))
{
userShares.AddRange(value);
}
}
if (await CanAsync(entry, userId, action, userShares))
{
result.Add(userId);
}
}
@ -595,13 +606,18 @@ public class FileSecurity : IFileSecurity
private async ValueTask<IAsyncEnumerable<Guid>> ToGuidAsync(FileShareRecord x)
{
if (x.SubjectType != SubjectType.Group)
{
return new[] { x.Subject }.ToAsyncEnumerable();
}
var groupInfo = await _userManager.GetGroupInfoAsync(x.Subject);
if (groupInfo.ID != Constants.LostGroupInfo.ID)
{
return (await _userManager.GetUsersByGroupAsync(groupInfo.ID))
.Where(p => p.Status == EmployeeStatus.Active)
.Select(y => y.Id).ToAsyncEnumerable();
.Where(p => p.Status == EmployeeStatus.Active)
.Select(y => y.Id).ToAsyncEnumerable();
}
return new[] { x.Subject }.ToAsyncEnumerable();
@ -915,10 +931,7 @@ public class FileSecurity : IFileSecurity
if (shares == null)
{
subjects = await GetUserSubjectsAsync(userId);
shares = (await GetSharesAsync(e))
.Join(subjects, r => r.Subject, s => s, (r, s) => r)
.ToList();
// shares ordered by level
shares = await GetSharesAsync(e, subjects);
}
if (e.FileEntryType == FileEntryType.File)
@ -1160,9 +1173,9 @@ public class FileSecurity : IFileSecurity
await securityDao.SetShareAsync(r);
}
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync<T>(FileEntry<T> entry)
public async Task<IEnumerable<FileShareRecord>> GetSharesAsync<T>(FileEntry<T> entry, IEnumerable<Guid> subjects = null)
{
return await _daoFactory.GetSecurityDao<T>().GetSharesAsync(entry);
return await _daoFactory.GetSecurityDao<T>().GetSharesAsync(entry, subjects);
}
public async IAsyncEnumerable<FileEntry> GetSharesForMeAsync(FilterType filterType, bool subjectGroup, Guid subjectID, string searchText = "", bool searchInContent = false, bool withSubfolders = false)
@ -1706,17 +1719,23 @@ public class FileSecurity : IFileSecurity
return entry.CreateBy == userId;
}
if (_cachedFullAccess.TryGetValue(GetCacheKey(entry.ParentId, userId), out var value))
if (_cachedRoomOwner.TryGetValue(GetCacheKey(entry.ParentId), out var roomOwner))
{
return value;
return roomOwner == userId;
}
var entryInMyRoom = await _daoFactory.GetFolderDao<T>().GetParentFoldersAsync(entry.ParentId)
.Where(f => DocSpaceHelper.IsRoom(f.FolderType) && f.CreateBy == userId).FirstOrDefaultAsync() != null;
var room = await _daoFactory.GetFolderDao<T>().GetParentFoldersAsync(entry.ParentId)
.Where(f => DocSpaceHelper.IsRoom(f.FolderType))
.FirstOrDefaultAsync();
_cachedFullAccess.TryAdd(GetCacheKey(entry.ParentId, userId), entryInMyRoom);
if (room == null)
{
return false;
}
return entryInMyRoom;
_cachedRoomOwner.TryAdd(GetCacheKey(entry.ParentId), room.CreateBy);
return room.CreateBy == userId;
}
private async Task<bool> IsAllGeneralNotificationSettingsOffAsync()
@ -1738,6 +1757,11 @@ public class FileSecurity : IFileSecurity
return $"{_tenantManager.GetCurrentTenant().Id}-{userId}-{parentId}";
}
private string GetCacheKey<T>(T parentId)
{
return $"{_tenantManager.GetCurrentTenant().Id}-{parentId}";
}
private sealed class SubjectComparer : IComparer<FileShareRecord>
{
private readonly List<Guid> _subjects;

View File

@ -30,12 +30,12 @@ namespace ASC.Files.Core.Security;
public interface ISecurityDao<T>
{
Task SetShareAsync(FileShareRecord r);
IAsyncEnumerable<FileShareRecord> GetShareForEntryIdsAsync(Guid subject, IEnumerable<string> roomIds);
IAsyncEnumerable<FileShareRecord> GetShareForEntryIdsAsync(Guid subject, IEnumerable<string> roomIds);
IAsyncEnumerable<FileShareRecord> GetSharesAsync(IEnumerable<Guid> subjects);
Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<T> entry);
Task<IEnumerable<FileShareRecord>> GetSharesAsync(FileEntry<T> entry, IEnumerable<Guid> subjects = null);
Task RemoveSubjectAsync(Guid subject);
IAsyncEnumerable<FileShareRecord> GetPureShareRecordsAsync(IEnumerable<FileEntry<T>> entries);
IAsyncEnumerable<FileShareRecord> GetPureShareRecordsAsync(FileEntry<T> entry);
Task DeleteShareRecordsAsync(IEnumerable<FileShareRecord> records);
Task<bool> IsSharedAsync(T entryId, FileEntryType type);
}
}

View File

@ -28,8 +28,8 @@ namespace ASC.Files.Core.Security;
public enum SubjectType
{
UserOrGroup = 0,
User = 0,
ExternalLink = 1,
Restriction = 2,
Group = 2,
InvitationLink = 3,
}

View File

@ -59,5 +59,9 @@ public class FilesMappingProfile : AutoMapper.Profile
CreateMap<DbFilesSecurity, FileShareRecord>()
.ForMember(dest => dest.Options, cfg =>
cfg.MapFrom(src => JsonSerializer.Deserialize<FileShareOptions>(src.Options, JsonSerializerOptions.Default)));
CreateMap<SecurityTreeRecord, FileShareRecord>()
.ForMember(dest => dest.Options, cfg =>
cfg.MapFrom(src => JsonSerializer.Deserialize<FileShareOptions>(src.Options, JsonSerializerOptions.Default)));
}
}

View File

@ -619,10 +619,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
newFile = await FileDao.CopyFileAsync(file.Id, toFolderId); //Stream copy will occur inside dao
_ = filesMessageService.SendAsync(newFile, toFolder, _headers, MessageAction.FileCopied, newFile.Title, parentFolder.Title, toFolder.Title);
if (Equals(newFile.ParentId.ToString(), _daoFolderId))
{
needToMark.Add(newFile);
}
needToMark.Add(newFile);
await socketManager.CreateFileAsync(newFile);

View File

@ -25,7 +25,7 @@
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Web.Files.Utils;
[Singletone]
public class FileMarkerHelper
{
@ -33,7 +33,7 @@ public class FileMarkerHelper
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
public DistributedTaskQueue Tasks { get; set; }
public FileMarkerHelper(
IServiceProvider serviceProvider,
ILogger<FileMarkerHelper> logger,
@ -43,12 +43,12 @@ public class FileMarkerHelper
_logger = logger;
Tasks = queueFactory.CreateQueue(CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME);
}
internal void Add<T>(AsyncTaskData<T> taskData)
{
Tasks.EnqueueTask(async (d, c) => await ExecMarkFileAsNewAsync(taskData), taskData);
}
private async Task ExecMarkFileAsNewAsync<T>(AsyncTaskData<T> obj)
{
try
@ -64,14 +64,14 @@ public class FileMarkerHelper
}
}
}
[Scope(Additional = typeof(FileMarkerExtention))]
public class FileMarker
{
private readonly ICache _cache;
private const string CacheKeyFormat = "MarkedAsNew/{0}/folder_{1}";
private readonly TenantManager _tenantManager;
private readonly UserManager _userManager;
private readonly IDaoFactory _daoFactory;
@ -106,14 +106,14 @@ public class FileMarker
_roomsNotificationSettingsHelper = roomsNotificationSettingsHelper;
_cache = cache;
}
internal async Task ExecMarkFileAsNewAsync<T>(AsyncTaskData<T> obj, SocketManager socketManager)
{
await _tenantManager.SetCurrentTenantAsync(obj.TenantID);
var folderDao = _daoFactory.GetFolderDao<T>();
T parentFolderId;
if (obj.FileEntry.FileEntryType == FileEntryType.File)
{
parentFolderId = ((File<T>)obj.FileEntry).ParentId;
@ -122,26 +122,26 @@ public class FileMarker
{
parentFolderId = ((Folder<T>)obj.FileEntry).Id;
}
var parentFolders = await folderDao.GetParentFoldersAsync(parentFolderId).Reverse().ToListAsync();
var userIDs = obj.UserIDs;
var userEntriesData = new Dictionary<Guid, List<FileEntry>>();
if (obj.FileEntry.RootFolderType == FolderType.BUNCH)
{
if (userIDs.Count == 0)
{
return;
}
var projectsFolder = await _globalFolder.GetFolderProjectsAsync<T>(_daoFactory);
parentFolders.Add(await folderDao.GetFolderAsync(projectsFolder));
var entries = new List<FileEntry> { obj.FileEntry };
entries = entries.Concat(parentFolders).ToList();
userIDs.ForEach(userID =>
{
if (userEntriesData.TryGetValue(userID, out var value))
@ -152,19 +152,20 @@ public class FileMarker
{
userEntriesData.Add(userID, entries);
}
RemoveFromCahce(projectsFolder, userID);
});
}
else
{
var filesSecurity = _fileSecurity;
if (userIDs.Count == 0)
{
var guids = await filesSecurity.WhoCanReadAsync(obj.FileEntry);
userIDs = guids.Where(x => x != obj.CurrentAccountId).ToList();
}
if (obj.FileEntry.ProviderEntry)
{
userIDs = await userIDs.ToAsyncEnumerable().WhereAwait(async u => !await _userManager.IsUserAsync(u)).ToListAsync();
@ -203,15 +204,15 @@ public class FileMarker
{
userEntriesData.Add(id, new List<FileEntry> { parentFolder });
}
}
}
}
if (obj.FileEntry.RootFolderType == FolderType.USER)
{
var folderDaoInt = _daoFactory.GetFolderDao<int>();
var folderShare = await folderDaoInt.GetFolderAsync(await _globalFolder.GetFolderShareAsync(_daoFactory));
foreach (var userID in userIDs)
{
var userFolderId = await folderDaoInt.GetFolderIDUserAsync(false, userID);
@ -219,13 +220,13 @@ public class FileMarker
{
continue;
}
Folder<int> rootFolder = null;
if (obj.FileEntry.ProviderEntry)
{
rootFolder = obj.FileEntry.RootCreateBy == userID
? await folderDaoInt.GetFolderAsync(userFolderId)
: folderShare;
? await folderDaoInt.GetFolderAsync(userFolderId)
: folderShare;
}
else if (!Equals(obj.FileEntry.RootId, userFolderId))
{
@ -235,12 +236,12 @@ public class FileMarker
{
RemoveFromCahce(userFolderId, userID);
}
if (rootFolder == null)
{
continue;
}
if (userEntriesData.TryGetValue(userID, out var value))
{
value.Add(rootFolder);
@ -249,7 +250,7 @@ public class FileMarker
{
userEntriesData.Add(userID, new List<FileEntry> { rootFolder });
}
RemoveFromCahce(rootFolder.Id, userID);
}
}
@ -257,7 +258,7 @@ public class FileMarker
{
var commonFolderId = await _globalFolder.GetFolderCommonAsync(this, _daoFactory);
userIDs.ForEach(userID => RemoveFromCahce(commonFolderId, userID));
if (obj.FileEntry.ProviderEntry)
{
var commonFolder = await folderDao.GetFolderAsync(await _globalFolder.GetFolderCommonAsync<T>(this, _daoFactory));
@ -271,7 +272,7 @@ public class FileMarker
{
userEntriesData.Add(userID, new List<FileEntry> { commonFolder });
}
RemoveFromCahce(commonFolderId, userID);
});
}
@ -338,7 +339,7 @@ public class FileMarker
RemoveFromCahce(rootFolder.Id, userID);
}
}
userIDs.ForEach(userID =>
{
if (userEntriesData.TryGetValue(userID, out var value))
@ -351,67 +352,65 @@ public class FileMarker
}
});
}
var tagDao = _daoFactory.GetTagDao<T>();
var newTags = new List<Tag>();
var updateTags = new List<Tag>();
try
{
await _semaphore.WaitAsync();
foreach (var userID in userEntriesData.Keys)
{
if (await tagDao.GetNewTagsAsync(userID, obj.FileEntry).AnyAsync())
foreach (var userId in userEntriesData.Keys)
{
continue;
}
var entries = userEntriesData[userID].Distinct().ToList();
await GetNewTagsAsync(userID, entries.OfType<FileEntry<int>>().ToList());
await GetNewTagsAsync(userID, entries.OfType<FileEntry<string>>().ToList());
}
if (await tagDao.GetNewTagsAsync(userId, obj.FileEntry).AnyAsync())
{
continue;
}
if (updateTags.Count > 0)
{
await tagDao.UpdateNewTags(updateTags, obj.CurrentAccountId);
}
if (newTags.Count > 0)
{
await tagDao.SaveTags(newTags, obj.CurrentAccountId);
}
}
catch
{
throw;
var entries = userEntriesData[userId].Distinct().ToList();
await GetNewTagsAsync(userId, entries.OfType<FileEntry<int>>().ToList());
await GetNewTagsAsync(userId, entries.OfType<FileEntry<string>>().ToList());
}
if (updateTags.Count > 0)
{
await tagDao.IncrementNewTagsAsync(updateTags, obj.CurrentAccountId);
}
if (newTags.Count > 0)
{
await tagDao.SaveTags(newTags, obj.CurrentAccountId);
}
}
finally
{
_semaphore.Release();
}
await Task.WhenAll(ExecMarkAsNewRequest(updateTags.Concat(newTags), socketManager));
await SendChangeNoticeAsync(updateTags.Concat(newTags), socketManager);
async Task GetNewTagsAsync<T1>(Guid userID, List<FileEntry<T1>> entries)
return;
async Task GetNewTagsAsync<T1>(Guid userId, List<FileEntry<T1>> entries)
{
var tagDao1 = _daoFactory.GetTagDao<T1>();
var exist = await tagDao1.GetNewTagsAsync(userID, entries).ToListAsync();
var exist = await tagDao1.GetNewTagsAsync(userId, entries).ToListAsync();
var update = exist.Where(t => t.EntryType == FileEntryType.Folder).ToList();
update.ForEach(t => t.Count++);
updateTags.AddRange(update);
entries.ForEach(entry =>
{
if (entry != null && exist.All(tag => tag != null && !tag.EntryId.Equals(entry.Id)))
{
newTags.Add(Tag.New(userID, entry));
newTags.Add(Tag.New(userId, entry));
}
});
}
}
public async ValueTask MarkAsNewAsync<T>(FileEntry<T> fileEntry, List<Guid> userIDs = null)
{
if (fileEntry == null)
@ -420,36 +419,36 @@ public class FileMarker
}
userIDs ??= new List<Guid>();
var taskData = _serviceProvider.GetService<AsyncTaskData<T>>();
taskData.FileEntry = (FileEntry<T>)fileEntry.Clone();
taskData.UserIDs = userIDs;
if (fileEntry.RootFolderType == FolderType.BUNCH && userIDs.Count == 0)
{
var folderDao = _daoFactory.GetFolderDao<T>();
var path = await folderDao.GetBunchObjectIDAsync(fileEntry.RootId);
var projectID = path.Split('/').Last();
if (string.IsNullOrEmpty(projectID))
{
return;
}
var whoCanRead = await _fileSecurity.WhoCanReadAsync(fileEntry);
var projectTeam = whoCanRead.Where(x => x != _authContext.CurrentAccount.ID).ToList();
if (projectTeam.Count == 0)
{
return;
}
taskData.UserIDs = projectTeam;
}
_serviceProvider.GetService<FileMarkerHelper>().Add(taskData);
}
public async ValueTask RemoveMarkAsNewAsync<T>(FileEntry<T> fileEntry, Guid userID = default)
{
if (fileEntry == null)
@ -458,41 +457,41 @@ public class FileMarker
}
userID = userID.Equals(default) ? _authContext.CurrentAccount.ID : userID;
var tagDao = _daoFactory.GetTagDao<T>();
var internalFolderDao = _daoFactory.GetFolderDao<int>();
var folderDao = _daoFactory.GetFolderDao<T>();
if (!await tagDao.GetNewTagsAsync(userID, fileEntry).AnyAsync())
{
return;
}
T folderID;
int valueNew;
var userFolderId = await internalFolderDao.GetFolderIDUserAsync(false, userID);
var privacyFolderId = await internalFolderDao.GetFolderIDPrivacyAsync(false, userID);
var removeTags = new List<Tag>();
if (fileEntry.FileEntryType == FileEntryType.File)
{
folderID = ((File<T>)fileEntry).ParentId;
removeTags.Add(Tag.New(userID, fileEntry));
valueNew = 1;
}
else
{
folderID = fileEntry.Id;
var listTags = await tagDao.GetNewTagsAsync(userID, (Folder<T>)fileEntry, true).ToListAsync();
valueNew = listTags.FirstOrDefault(tag => tag.EntryId.Equals(fileEntry.Id)).Count;
if (Equals(fileEntry.Id, userFolderId) || Equals(fileEntry.Id, await _globalFolder.GetFolderCommonAsync(this, _daoFactory)) || Equals(fileEntry.Id, await _globalFolder.GetFolderShareAsync(_daoFactory)))
{
var folderTags = listTags.Where(tag => tag.EntryType == FileEntryType.Folder);
foreach (var tag in folderTags)
{
var folderEntry = await folderDao.GetFolderAsync((T)tag.EntryId);
@ -500,15 +499,15 @@ public class FileMarker
{
listTags.Remove(tag);
listTags.AddRange(await tagDao.GetNewTagsAsync(userID, folderEntry, true).ToListAsync());
}
}
}
}
}
removeTags.AddRange(listTags);
}
var parentFolders = await folderDao.GetParentFoldersAsync(folderID).Reverse().ToListAsync();
var rootFolder = parentFolders.LastOrDefault();
int rootFolderId = default;
int cacheFolderId = default;
@ -561,24 +560,24 @@ public class FileMarker
{
cacheFolderId = await _globalFolder.GetFolderShareAsync(_daoFactory);
}
var updateTags = new List<Tag>();
if (!rootFolderId.Equals(default))
{
await UpdateRemoveTags(await internalFolderDao.GetFolderAsync(rootFolderId));
}
if (!cacheFolderId.Equals(default))
{
RemoveFromCahce(cacheFolderId, userID);
}
foreach (var parentFolder in parentFolders)
{
await UpdateRemoveTags(parentFolder);
}
if (updateTags.Count > 0)
{
await tagDao.UpdateNewTags(updateTags);
@ -597,18 +596,20 @@ public class FileMarker
EntryType = r.EntryType
});
await Task.WhenAll(ExecMarkAsNewRequest(updateTags.Concat(toRemove), socketManager));
await SendChangeNoticeAsync(updateTags.Concat(toRemove), socketManager);
return;
async Task UpdateRemoveTags<TFolder>(Folder<TFolder> folder)
{
var tagDao = _daoFactory.GetTagDao<TFolder>();
var newTags = tagDao.GetNewTagsAsync(userID, folder);
var parentTag = await newTags.FirstOrDefaultAsync();
if (parentTag != null)
{
parentTag.Count -= valueNew;
if (parentTag.Count > 0)
{
updateTags.Add(parentTag);
@ -620,21 +621,21 @@ public class FileMarker
}
}
}
public async Task RemoveMarkAsNewForAllAsync<T>(FileEntry<T> fileEntry)
{
IAsyncEnumerable<Guid> userIDs;
var tagDao = _daoFactory.GetTagDao<T>();
var tags = tagDao.GetTagsAsync(fileEntry.Id, fileEntry.FileEntryType == FileEntryType.File ? FileEntryType.File : FileEntryType.Folder, TagType.New);
userIDs = tags.Select(tag => tag.Owner).Distinct();
await foreach (var userID in userIDs)
{
await RemoveMarkAsNewAsync(fileEntry, userID);
}
}
public async Task<int> GetRootFoldersIdMarkedAsNewAsync<T>(T rootId)
{
@ -647,24 +648,24 @@ public class FileMarker
var requestTag = await requestTags.FirstOrDefaultAsync(tag => tag.EntryId.Equals(rootId));
var count = requestTag == null ? 0 : requestTag.Count;
InsertToCahce(rootId, count);
return count;
}
else if (fromCache > 0)
{
return fromCache;
}
return 0;
}
public IAsyncEnumerable<FileEntry> MarkedItemsAsync<T>(Folder<T> folder)
{
if (folder == null)
{
throw new ArgumentNullException(nameof(folder), FilesCommonResource.ErrorMassage_FolderNotFound);
}
return InternalMarkedItemsAsync(folder);
}
@ -675,67 +676,67 @@ public class FileMarker
throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ViewFolder);
}
if (folder.RootFolderType == FolderType.TRASH && !Equals(folder.Id, await _globalFolder.GetFolderTrashAsync(_daoFactory)))
{
throw new SecurityException(FilesCommonResource.ErrorMassage_ViewTrashItem);
}
if (folder.RootFolderType == FolderType.TRASH && !Equals(folder.Id, await _globalFolder.GetFolderTrashAsync(_daoFactory)))
{
throw new SecurityException(FilesCommonResource.ErrorMassage_ViewTrashItem);
}
var tagDao = _daoFactory.GetTagDao<T>();
var providerFolderDao = _daoFactory.GetFolderDao<string>();
var providerTagDao = _daoFactory.GetTagDao<string>();
var tags = await (tagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, folder, true) ?? AsyncEnumerable.Empty<Tag>()).ToListAsync();
var tagDao = _daoFactory.GetTagDao<T>();
var providerFolderDao = _daoFactory.GetFolderDao<string>();
var providerTagDao = _daoFactory.GetTagDao<string>();
var tags = await (tagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, folder, true) ?? AsyncEnumerable.Empty<Tag>()).ToListAsync();
if (!tags.Any())
{
yield break;
}
if (Equals(folder.Id, await _globalFolder.GetFolderMyAsync(this, _daoFactory)) ||
Equals(folder.Id, await _globalFolder.GetFolderCommonAsync(this, _daoFactory)) ||
Equals(folder.Id, await _globalFolder.GetFolderShareAsync(_daoFactory)) ||
Equals(folder.Id, await _globalFolder.GetFolderVirtualRoomsAsync(_daoFactory)))
{
var folderTags = tags.Where(tag => tag.EntryType == FileEntryType.Folder && (tag.EntryId is string));
var providerFolderTags = new List<KeyValuePair<Tag, Folder<string>>>();
foreach (var tag in folderTags)
{
var pair = new KeyValuePair<Tag, Folder<string>>(tag, await providerFolderDao.GetFolderAsync(tag.EntryId.ToString()));
if (pair.Value != null && pair.Value.ProviderEntry)
{
providerFolderTags.Add(pair);
}
}
providerFolderTags.Reverse();
foreach (var providerFolderTag in providerFolderTags)
var providerFolderTags = new List<KeyValuePair<Tag, Folder<string>>>();
foreach (var tag in folderTags)
{
tags.AddRange(await providerTagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, providerFolderTag.Value, true).ToListAsync());
var pair = new KeyValuePair<Tag, Folder<string>>(tag, await providerFolderDao.GetFolderAsync(tag.EntryId.ToString()));
if (pair.Value != null && pair.Value.ProviderEntry)
{
providerFolderTags.Add(pair);
}
}
providerFolderTags.Reverse();
foreach (var providerFolderTag in providerFolderTags)
{
tags.AddRange(await providerTagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, providerFolderTag.Value, true).ToListAsync());
}
}
tags = tags
.Where(r => !Equals(r.EntryId, folder.Id))
.Distinct()
.Distinct()
.ToList();
//TODO: refactoring
var entryTagsProvider = await GetEntryTagsAsync<string>(tags.Where(r => r.EntryId is string).ToAsyncEnumerable());
var entryTagsInternal = await GetEntryTagsAsync<int>(tags.Where(r => r.EntryId is int).ToAsyncEnumerable());
var entryTagsInternal = await GetEntryTagsAsync<int>(tags.Where(r => r.EntryId is int).ToAsyncEnumerable());
foreach (var entryTag in entryTagsInternal)
{
var parentEntry = entryTagsInternal.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.Id, entryTag.Key.ParentId));
if (parentEntry != null)
{
entryTagsInternal[parentEntry].Count -= entryTag.Value.Count;
}
}
foreach (var entryTag in entryTagsProvider)
{
if (int.TryParse(entryTag.Key.ParentId, out var fId))
@ -746,20 +747,20 @@ public class FileMarker
if (parentEntryInt != null)
{
entryTagsInternal[parentEntryInt].Count -= entryTag.Value.Count;
}
}
continue;
}
var parentEntry = entryTagsProvider.Keys
.FirstOrDefault(entryCountTag => Equals(entryCountTag.Id, entryTag.Key.ParentId));
if (parentEntry != null)
{
entryTagsProvider[parentEntry].Count -= entryTag.Value.Count;
}
}
}
await foreach (var r in GetResultAsync(entryTagsInternal))
{
yield return r;
@ -772,28 +773,28 @@ public class FileMarker
async IAsyncEnumerable<FileEntry> GetResultAsync<TEntry>(Dictionary<FileEntry<TEntry>, Tag> entryTags)
{
foreach (var entryTag in entryTags)
{
if (!string.IsNullOrEmpty(entryTag.Key.Error))
{
await RemoveMarkAsNewAsync(entryTag.Key);
continue;
}
if (entryTag.Value.Count > 0)
foreach (var entryTag in entryTags)
{
if (!string.IsNullOrEmpty(entryTag.Key.Error))
{
yield return entryTag.Key;
}
}
}
await RemoveMarkAsNewAsync(entryTag.Key);
continue;
}
if (entryTag.Value.Count > 0)
{
yield return entryTag.Key;
}
}
}
}
private async Task<Dictionary<FileEntry<T>, Tag>> GetEntryTagsAsync<T>(IAsyncEnumerable<Tag> tags)
{
var fileDao = _daoFactory.GetFileDao<T>();
var folderDao = _daoFactory.GetFolderDao<T>();
var entryTags = new Dictionary<FileEntry<T>, Tag>();
await foreach (var tag in tags)
{
var entry = tag.EntryType == FileEntryType.File
@ -817,7 +818,7 @@ public class FileMarker
var tagDao = _daoFactory.GetTagDao<T>();
var folderDao = _daoFactory.GetFolderDao<T>();
var totalTags = await tagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, parent, false).ToListAsync();
if (totalTags.Count <= 0)
{
return;
@ -977,13 +978,13 @@ public class FileMarker
}
}
}
private void InsertToCahce(object folderId, int count)
{
var key = string.Format(CacheKeyFormat, _authContext.CurrentAccount.ID, folderId);
_cache.Insert(key, count.ToString(), TimeSpan.FromMinutes(10));
}
private int GetCountFromCahce(object folderId)
{
var key = string.Format(CacheKeyFormat, _authContext.CurrentAccount.ID, folderId);
@ -991,34 +992,34 @@ public class FileMarker
return count == null ? -1 : int.Parse(count);
}
private void RemoveFromCahce(object folderId)
{
RemoveFromCahce(folderId, _authContext.CurrentAccount.ID);
}
private void RemoveFromCahce(object folderId, Guid userId)
{
var key = string.Format(CacheKeyFormat, userId, folderId);
_cache.Remove(key);
}
private IEnumerable<Task> ExecMarkAsNewRequest(IEnumerable<Tag> tags, SocketManager socketManager)
private static async Task SendChangeNoticeAsync(IEnumerable<Tag> tags, SocketManager socketManager)
{
foreach (var t in tags)
const int chunkSize = 1000;
foreach (var chunk in tags.Where(t => t.EntryType == FileEntryType.File).Chunk(chunkSize))
{
if (t.EntryType == FileEntryType.File)
{
yield return socketManager.ExecMarkAsNewFileAsync(t.EntryId, t.Count, t.Owner);
}
else if (t.EntryType == FileEntryType.Folder)
{
yield return socketManager.ExecMarkAsNewFolderAsync(t.EntryId, t.Count, t.Owner);
}
await socketManager.ExecMarkAsNewFilesAsync(chunk);
}
foreach (var chunk in tags.Where(t => t.EntryType == FileEntryType.Folder).Chunk(chunkSize))
{
await socketManager.ExecMarkAsNewFoldersAsync(chunk);
}
}
}
[Transient]
public class AsyncTaskData<T> : DistributedTask
{
@ -1027,13 +1028,13 @@ public class AsyncTaskData<T> : DistributedTask
TenantID = tenantManager.GetCurrentTenant().Id;
CurrentAccountId = authContext.CurrentAccount.ID;
}
public int TenantID { get; private set; }
public FileEntry<T> FileEntry { get; set; }
public List<Guid> UserIDs { get; set; }
public Guid CurrentAccountId { get; set; }
}
public static class FileMarkerExtention
{
public static void Register(DIHelper services)
@ -1043,4 +1044,4 @@ public static class FileMarkerExtention
services.TryAdd<AsyncTaskData<string>>();
}
}
}

View File

@ -555,7 +555,7 @@ public class FileSharing
FileShareOptions = r.Options,
};
w.CanEditAccess = _authContext.CurrentAccount.ID != w.Id && w.SubjectType == SubjectType.UserOrGroup && canEditAccess;
w.CanEditAccess = _authContext.CurrentAccount.ID != w.Id && w.SubjectType is SubjectType.User or SubjectType.Group && canEditAccess;
if (isRoom && r.IsLink)
{
@ -632,7 +632,7 @@ public class FileSharing
result.Add(w);
}
if (!result.Any(w => w.Owner) && (subjectsTypes == null || subjectsTypes.Contains(SubjectType.UserOrGroup)))
if (!result.Any(w => w.Owner) && (subjectsTypes == null || subjectsTypes.Contains(SubjectType.User) || subjectsTypes.Contains(SubjectType.Group)))
{
var ownerId = entry.RootFolderType == FolderType.USER ? entry.RootCreateBy : entry.CreateBy;
var w = new AceWrapper

View File

@ -33,7 +33,7 @@ public class SocketManager : SocketServiceClient
private readonly TenantManager _tenantManager;
public override string Hub => "files";
public SocketManager(
ILogger<SocketServiceClient> logger,
IHttpClientFactory clientFactory,
@ -47,7 +47,7 @@ public class SocketManager : SocketServiceClient
_tenantManager = tenantManager;
_folderDtoHelper = folderDtoHelper;
}
public async Task StartEditAsync<T>(T fileId)
{
var room = await GetFileRoomAsync(fileId);
@ -110,25 +110,39 @@ public class SocketManager : SocketServiceClient
await MakeRequest("delete-folder", new { room, folderId = folder.Id });
}
public async Task ExecMarkAsNewFileAsync(object fileId, int count, Guid owner)
public async Task ExecMarkAsNewFilesAsync(IEnumerable<Tag> tags)
{
var room = await GetFileRoomAsync(fileId, owner);
var result = await tags.ToAsyncEnumerable()
.SelectAwait(async tag => new
{
room = await GetFileRoomAsync(tag.EntryId, tag.Owner),
fileId = tag.EntryId,
count = tag.Count
})
.ToListAsync();
await MakeRequest("markasnew-file", new { room, fileId, count });
SendNotAwaitableRequest("markasnew-file", result);
}
public async Task ExecMarkAsNewFolderAsync(object folderId, int count, Guid owner)
public async Task ExecMarkAsNewFoldersAsync(IEnumerable<Tag> tags)
{
var room = await GetFolderRoomAsync(folderId, owner);
var result = await tags.ToAsyncEnumerable()
.SelectAwait(async tag => new
{
room = await GetFolderRoomAsync(tag.EntryId, tag.Owner),
folderId = tag.EntryId,
count = tag.Count
})
.ToListAsync();
await MakeRequest("markasnew-folder", new { room, folderId, count });
SendNotAwaitableRequest("markasnew-folder", result);
}
private async Task<string> GetFileRoomAsync<T>(T fileId, Guid? owner = null)
{
var tenantId = await _tenantManager.GetCurrentTenantIdAsync();
var ownerData = owner.HasValue ? "-" + owner.Value : "";
return $"{tenantId}-FILE-{fileId}{ownerData}";
}
@ -136,7 +150,7 @@ public class SocketManager : SocketServiceClient
{
var tenantId = await _tenantManager.GetCurrentTenantIdAsync();
var ownerData = owner.HasValue ? "-" + owner.Value : "";
return $"{tenantId}-DIR-{folderId}{ownerData}";
}
@ -162,4 +176,4 @@ public class SocketManager : SocketServiceClient
serializerSettings.Converters.Add(new FileEntryWrapperConverter());
return serializerSettings;
}
}
}