Merge branch 'develop' into feature/sso-mobile-layout
This commit is contained in:
commit
2cc96cdb2c
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
@ -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
|
||||
};
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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 };
|
||||
|
@ -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,
|
||||
|
@ -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)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,10 @@ const MainContainer = styled.div`
|
||||
position: absolute;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
.history-row-container {
|
||||
max-width: 700px;
|
||||
}
|
||||
}
|
||||
|
||||
${(props) => props.isSettingNotPaid && UnavailableStyles}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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`
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ namespace ASC.Files.Core.Security;
|
||||
|
||||
public enum SubjectType
|
||||
{
|
||||
UserOrGroup = 0,
|
||||
User = 0,
|
||||
ExternalLink = 1,
|
||||
Restriction = 2,
|
||||
Group = 2,
|
||||
InvitationLink = 3,
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user