Merge pull request #39 from ONLYOFFICE/feature/feed

Feature/feed
This commit is contained in:
Pavel Bannov 2020-06-16 17:44:56 +03:00 committed by GitHub
commit dedd754875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1258 additions and 11 deletions

View File

@ -52,10 +52,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppLimit.CloudComputing.Sha
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Files.Service", "products\ASC.Files\Service\ASC.Files.Service.csproj", "{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.ApiSystem", "common\services\ASC.ApiSystem\ASC.ApiSystem.csproj", "{C2BB03A0-C35B-433F-96D4-3A06CBC06AD7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.ApiSystem", "common\services\ASC.ApiSystem\ASC.ApiSystem.csproj", "{C2BB03A0-C35B-433F-96D4-3A06CBC06AD7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.UrlShortener.Svc", "common\services\ASC.UrlShortener.Svc\ASC.UrlShortener.Svc.csproj", "{04A56018-C41E-4634-A185-A13E9250C75A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.Feed.Aggregator", "common\services\ASC.Feed.Aggregator\ASC.Feed.Aggregator.csproj", "{07CCC11F-76CB-448E-B15A-72E09FBB348B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -162,6 +164,10 @@ Global
{04A56018-C41E-4634-A185-A13E9250C75A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04A56018-C41E-4634-A185-A13E9250C75A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04A56018-C41E-4634-A185-A13E9250C75A}.Release|Any CPU.Build.0 = Release|Any CPU
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07CCC11F-76CB-448E-B15A-72E09FBB348B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -231,6 +231,13 @@ namespace ASC.Core.Common
return baseUri.ToString().TrimEnd('/');
}
public void Initialize(string serverUri)
{
var uri = new Uri(serverUri.Replace('*', 'x').Replace('+', 'x'));
_serverRoot = new UriBuilder(uri.Scheme, LOCALHOST, uri.Port);
_vpath = "/" + uri.AbsolutePath.Trim('/');
}
}
public static class BaseCommonLinkUtilityExtension

View File

@ -74,6 +74,16 @@ namespace ASC.Core
}
public UserManager(
IHttpContextAccessor httpContextAccessor,
IUserService service,
TenantManager tenantManager,
PermissionContext permissionContext,
UserManagerConstants userManagerConstants) : this(service, tenantManager, permissionContext, userManagerConstants)
{
Accessor = httpContextAccessor;
}
public UserManager(
IUserService service,
TenantManager tenantManager,

View File

@ -8,7 +8,10 @@ namespace ASC.Core.Common.EF.Model
[Table("feed_users")]
public class FeedUsers : BaseEntity
{
[Column("feed_id")]
public string FeedId { get; set; }
[Column("user_id")]
public Guid UserId { get; set; }
public override object[] GetKeys() => new object[] { FeedId, UserId };

View File

@ -49,7 +49,8 @@ namespace ASC.Feed.Data
public TenantUtil TenantUtil { get; }
public FeedDbContext FeedDbContext { get; }
public FeedAggregateDataProvider(DbContextManager<FeedDbContext> dbContextManager)
public FeedAggregateDataProvider(AuthContext authContext, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FeedDbContext> dbContextManager)
: this(authContext, tenantManager, tenantUtil)
{
FeedDbContext = dbContextManager.Get(Constants.FeedDbId);
}
@ -124,7 +125,10 @@ namespace ASC.Feed.Data
if (f.ClearRightsBeforeInsert)
{
var fu = FeedDbContext.FeedUsers.Where(r => r.FeedId == f.Id).FirstOrDefault();
FeedDbContext.FeedUsers.Remove(fu);
if (fu != null)
{
FeedDbContext.FeedUsers.Remove(fu);
}
}
FeedDbContext.AddOrUpdate(r => r.FeedAggregates, feedAggregate);
@ -382,6 +386,8 @@ namespace ASC.Feed.Data
{
public static DIHelper AddFeedAggregateDataProvider(this DIHelper services)
{
services.TryAddScoped<FeedAggregateDataProvider>();
return services
.AddAuthContextService()
.AddTenantManagerService()

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\web\ASC.Web.Core\ASC.Web.Core.csproj" />
<ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" />
<ProjectReference Include="..\..\ASC.Feed\ASC.Feed.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Modules\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,46 @@
using System;
using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
namespace ASC.Feed.Configuration
{
public class FeedSettings
{
public string ServerRoot { get; set; }
public TimeSpan AggregatePeriod { get; set; }
public TimeSpan AggregateInterval { get; set; }
public TimeSpan RemovePeriod { get; set; }
public static FeedSettings GetInstance(IConfiguration configuration)
{
var result = configuration.GetSetting<FeedSettings>("feed");
if (string.IsNullOrEmpty(result.ServerRoot))
{
result.ServerRoot = "http://*/";
}
if (result.AggregatePeriod == TimeSpan.Zero)
{
result.AggregatePeriod = TimeSpan.FromMinutes(5);
}
if (result.AggregateInterval == TimeSpan.Zero)
{
result.AggregateInterval = TimeSpan.FromDays(14);
}
if (result.RemovePeriod == TimeSpan.Zero)
{
result.RemovePeriod = TimeSpan.FromDays(1);
}
return result;
}
}
}

View File

@ -0,0 +1,131 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using ASC.Core;
using ASC.Feed.Data;
using ASC.Web.Core;
namespace ASC.Feed.Aggregator.Modules
{
public abstract class FeedModule : IFeedModule
{
public abstract string Name { get; }
public abstract string Product { get; }
public abstract Guid ProductID { get; }
protected abstract string DbId { get; }
protected int Tenant
{
get { return TenantManager.GetCurrentTenant().TenantId; }
}
public TenantManager TenantManager { get; }
public WebItemSecurity WebItemSecurity { get; }
public FeedModule(TenantManager tenantManager, WebItemSecurity webItemSecurity)
{
TenantManager = tenantManager;
WebItemSecurity = webItemSecurity;
}
protected string GetGroupId(string item, Guid author, string rootId = null, int action = -1)
{
const int interval = 2;
var now = DateTime.UtcNow;
var hours = now.Hour;
var groupIdHours = hours - (hours % interval);
if (rootId == null)
{
// groupId = {item}_{author}_{date}
return string.Format("{0}_{1}_{2}",
item,
author,
now.ToString("yyyy.MM.dd.") + groupIdHours);
}
if (action == -1)
{
// groupId = {item}_{author}_{date}_{rootId}_{action}
return string.Format("{0}_{1}_{2}_{3}",
item,
author,
now.ToString("yyyy.MM.dd.") + groupIdHours,
rootId);
}
// groupId = {item}_{author}_{date}_{rootId}_{action}
return string.Format("{0}_{1}_{2}_{3}_{4}",
item,
author,
now.ToString("yyyy.MM.dd.") + groupIdHours,
rootId,
action);
}
public abstract IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
public abstract IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
public virtual bool VisibleFor(Feed feed, object data, Guid userId)
{
return WebItemSecurity.IsAvailableForUser(ProductID, userId);
}
public virtual void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
{
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return;
foreach (var tuple in feed)
{
if (VisibleFor(tuple.Item1.Feed, tuple.Item2, userId))
{
tuple.Item1.Users.Add(userId);
}
}
}
protected static Guid ToGuid(object guid)
{
try
{
var str = guid as string;
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
}
catch (Exception)
{
return Guid.Empty;
}
}
}
}

View File

@ -0,0 +1,47 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using ASC.Feed.Data;
namespace ASC.Feed.Aggregator.Modules
{
public interface IFeedModule
{
string Name { get; }
string Product { get; }
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
bool VisibleFor(Feed feed, object data, Guid userId);
void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId);
}
}

View File

@ -0,0 +1,306 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.Logging;
using ASC.Core;
using ASC.Core.Common;
using ASC.Feed.Aggregator.Modules;
using ASC.Feed.Configuration;
using ASC.Feed.Data;
using Autofac;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
namespace ASC.Feed.Aggregator
{
public class FeedAggregatorService : IHostedService
{
private ILog Log { get; set; }
//private static readonly SignalrServiceClient signalrServiceClient = new SignalrServiceClient("counters");//TODO
private Timer aggregateTimer;
private Timer removeTimer;
private volatile bool isStopped;
private readonly object aggregateLock = new object();
private readonly object removeLock = new object();
public IConfiguration Configuration { get; }
public IServiceProvider ServiceProvider { get; }
public IContainer Container { get; }
public FeedAggregatorService(
IConfiguration configuration,
IServiceProvider serviceProvider,
IContainer container,
IOptionsMonitor<ILog> optionsMonitor)
{
Configuration = configuration;
ServiceProvider = serviceProvider;
Container = container;
Log = optionsMonitor.Get("ASC.Feed.Agregator");
}
public Task StartAsync(CancellationToken cancellationToken)
{
var cfg = FeedSettings.GetInstance(Configuration);
isStopped = false;
aggregateTimer = new Timer(AggregateFeeds, cfg.AggregateInterval, TimeSpan.Zero, cfg.AggregatePeriod);
removeTimer = new Timer(RemoveFeeds, cfg.AggregateInterval, cfg.RemovePeriod, cfg.RemovePeriod);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
isStopped = true;
if (aggregateTimer != null)
{
aggregateTimer.Change(Timeout.Infinite, Timeout.Infinite);
aggregateTimer.Dispose();
aggregateTimer = null;
}
if (removeTimer != null)
{
removeTimer.Change(Timeout.Infinite, Timeout.Infinite);
removeTimer.Dispose();
removeTimer = null;
}
return Task.CompletedTask;
}
private void AggregateFeeds(object interval)
{
if (!Monitor.TryEnter(aggregateLock)) return;
try
{
var cfg = FeedSettings.GetInstance(Configuration);
using var scope = ServiceProvider.CreateScope();
var commonLinkUtility = scope.ServiceProvider.GetService<BaseCommonLinkUtility>();
commonLinkUtility.Initialize(cfg.ServerRoot);
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
var start = DateTime.UtcNow;
Log.DebugFormat("Start of collecting feeds...");
var unreadUsers = new Dictionary<int, Dictionary<Guid, int>>();
using var autofacScope = Container.BeginLifetimeScope();
var modules = autofacScope.Resolve<IEnumerable<IFeedModule>>();
foreach (var module in modules)
{
var result = new List<FeedRow>();
var fromTime = feedAggregateDataProvider.GetLastTimeAggregate(module.GetType().Name);
if (fromTime == default) fromTime = DateTime.UtcNow.Subtract((TimeSpan)interval);
var toTime = DateTime.UtcNow;
var tenants = Attempt(10, () => module.GetTenantsWithFeeds(fromTime)).ToList();
Log.DebugFormat("Find {1} tenants for module {0}.", module.GetType().Name, tenants.Count());
foreach (var tenant in tenants)
{
// Warning! There is hack here!
// clearing the cache to get the correct acl
var cache = AscCache.Memory;
cache.Remove("acl" + tenant);
cache.Remove("/webitemsecurity/" + tenant);
//cache.Remove(string.Format("sub/{0}/{1}/{2}", tenant, "6045b68c-2c2e-42db-9e53-c272e814c4ad", NotifyConstants.Event_NewCommentForMessage.ID));
try
{
if (tenantManager.GetTenant(tenant) == null)
{
continue;
}
tenantManager.SetCurrentTenant(tenant);
var userManager = scope.ServiceProvider.GetService<UserManager>();
var securityContext = scope.ServiceProvider.GetService<SecurityContext>();
var authManager = scope.ServiceProvider.GetService<AuthManager>();
var users = userManager.GetUsers();
var feeds = Attempt(10, () => module.GetFeeds(new FeedFilter(fromTime, toTime) { Tenant = tenant }).Where(r => r.Item1 != null).ToList());
Log.DebugFormat("{0} feeds in {1} tenant.", feeds.Count, tenant);
var tenant1 = tenant;
var module1 = module;
var feedsRow = feeds
.Select(tuple => new Tuple<FeedRow, object>(new FeedRow(tuple.Item1)
{
Tenant = tenant1,
ProductId = module1.Product
}, tuple.Item2))
.ToList();
foreach (var u in users)
{
if (isStopped)
{
return;
}
if (!TryAuthenticate(securityContext, authManager, tenant1, u.ID))
{
continue;
}
module.VisibleFor(feedsRow, u.ID);
}
result.AddRange(feedsRow.Select(r => r.Item1));
}
catch (Exception ex)
{
Log.ErrorFormat("Tenant: {0}, {1}", tenant, ex);
}
}
feedAggregateDataProvider.SaveFeeds(result, module.GetType().Name, toTime);
foreach (var res in result)
{
foreach (var userGuid in res.Users.Where(userGuid => !userGuid.Equals(res.ModifiedById)))
{
if (!unreadUsers.TryGetValue(res.Tenant, out var dictionary))
{
dictionary = new Dictionary<Guid, int>();
}
if (dictionary.ContainsKey(userGuid))
{
++dictionary[userGuid];
}
else
{
dictionary.Add(userGuid, 1);
}
unreadUsers[res.Tenant] = dictionary;
}
}
}
//signalrServiceClient.SendUnreadUsers(unreadUsers);
Log.DebugFormat("Time of collecting news: {0}", DateTime.UtcNow - start);
}
catch (Exception ex)
{
Log.Error(ex);
}
finally
{
Monitor.Exit(aggregateLock);
}
}
private void RemoveFeeds(object interval)
{
if (!Monitor.TryEnter(removeLock)) return;
try
{
using var scope = ServiceProvider.CreateScope();
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
Log.DebugFormat("Start of removing old news");
feedAggregateDataProvider.RemoveFeedAggregate(DateTime.UtcNow.Subtract((TimeSpan)interval));
}
catch (Exception ex)
{
Log.Error(ex);
}
finally
{
Monitor.Exit(removeLock);
}
}
private static T Attempt<T>(int count, Func<T> action)
{
var counter = 0;
while (true)
{
try
{
return action();
}
catch
{
if (count < ++counter)
{
throw;
}
}
}
}
private static bool TryAuthenticate(SecurityContext securityContext, AuthManager authManager, int tenantId, Guid userid)
{
try
{
securityContext.AuthenticateMe(authManager.GetAccountByID(tenantId, userid));
return true;
}
catch
{
return false;
}
}
}
public static class FeedAggregatorServiceExtension
{
public static DIHelper AddFeedAggregatorService(this DIHelper services)
{
services.TryAddSingleton<FeedAggregatorService>();
return services
.AddBaseCommonLinkUtilityService()
.AddTenantManagerService()
.AddUserManagerService()
.AddSecurityContextService()
.AddAuthManager()
.AddFeedAggregateDataProvider();
}
}
}

View File

@ -28,6 +28,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using ASC.Files.Core.Security;
using ASC.Web.Files.Services.DocumentService;
namespace ASC.Files.Core
@ -303,6 +304,10 @@ namespace ASC.Files.Core
bool ContainChanges(T fileId, int fileVersion);
IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to);
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
#endregion
}
}

View File

@ -28,6 +28,8 @@ using System;
using System.Collections.Generic;
using System.Threading;
using ASC.Files.Core.Security;
namespace ASC.Files.Core
{
public interface IFolderDao<T>
@ -292,6 +294,11 @@ namespace ASC.Files.Core
/// <returns></returns>
Dictionary<string, string> GetBunchObjectIDs(List<T> folderIDs);
IEnumerable<(Folder<T>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to);
IEnumerable<T> GetTenantsWithFeeds(DateTime fromTime);
#endregion
}
}

View File

@ -38,6 +38,7 @@ using ASC.Core.Common.Settings;
using ASC.Core.Tenants;
using ASC.ElasticSearch;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Files.Thirdparty.ProviderDao;
@ -1204,6 +1205,51 @@ namespace ASC.Files.Core.Data
.Any();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
var q1 = FilesDbContext.Files
.Where(r => r.TenantId == tenant)
.Where(r => r.CurrentVersion)
.Where(r => r.ModifiedOn >= from && r.ModifiedOn <= to);
var q2 = FromQuery(q1)
.Select(r => new DbFileQueryWithSecurity() { DbFileQuery = r, Security = null });
var q3 = FilesDbContext.Files
.Where(r => r.TenantId == tenant)
.Where(r => r.CurrentVersion);
var q4 = FromQuery(q3)
.Join(FilesDbContext.Security.DefaultIfEmpty(), r => r.file.Id.ToString(), s => s.EntryId, (f, s) => new DbFileQueryWithSecurity { DbFileQuery = f, Security = s })
.Where(r => r.Security.TenantId == tenant)
.Where(r => r.Security.EntryType == FileEntryType.File)
.Where(r => r.Security.Security == Security.FileShare.Restrict)
.Where(r => r.Security.TimeStamp >= from && r.Security.TimeStamp <= to);
return q2.Select(ToFileWithShare).ToList().Union(q4.Select(ToFileWithShare).ToList());
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
var q1 = FilesDbContext.Files
.Where(r => r.ModifiedOn > fromTime)
.Select(r => r.TenantId)
.GroupBy(r => r)
.Where(r => r.Count() > 0)
.Select(r => r.Key)
.ToList();
var q2 = FilesDbContext.Security
.Where(r => r.TimeStamp > fromTime)
.Select(r => r.TenantId)
.GroupBy(r => r)
.Where(r => r.Count() > 0)
.Select(r => r.Key)
.ToList();
return q1.Union(q2);
}
#endregion
private Func<Selector<DbFile>, Selector<DbFile>> GetFuncForSearch(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false)
@ -1341,6 +1387,21 @@ namespace ASC.Files.Core.Data
return file;
}
public (File<int>, SmallShareRecord) ToFileWithShare(DbFileQueryWithSecurity r)
{
var file = ToFile(r.DbFileQuery);
var record = r.Security != null
? new SmallShareRecord
{
ShareOn = r.Security.TimeStamp,
ShareBy = r.Security.Owner,
ShareTo = r.Security.Subject
}
: null;
return (file, record);
}
internal protected DbFile InitDocument(DbFile dbFile)
{
if (!FactoryIndexer.CanSearchByContent())
@ -1379,6 +1440,12 @@ namespace ASC.Files.Core.Data
public bool shared { get; set; }
}
public class DbFileQueryWithSecurity
{
public DbFileQuery DbFileQuery { get; set; }
public DbFilesSecurity Security { get; set; }
}
public static class FileDaoExtention
{
public static DIHelper AddFileDaoService(this DIHelper services)

View File

@ -37,6 +37,7 @@ using ASC.Core.Common.Settings;
using ASC.Core.Tenants;
using ASC.ElasticSearch;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Files.Thirdparty.ProviderDao;
@ -1081,6 +1082,21 @@ namespace ASC.Files.Core.Data
return result;
}
public (Folder<int>, SmallShareRecord) ToFolderWithShare(DbFolderQueryWithSecurity r)
{
var file = ToFolder(r.DbFolderQuery);
var record = r.Security != null
? new SmallShareRecord
{
ShareOn = r.Security.TimeStamp,
ShareBy = r.Security.Owner,
ShareTo = r.Security.Subject
}
: null;
return (file, record);
}
public string GetBunchObjectID(int folderID)
{
return Query(FilesDbContext.BunchObjects)
@ -1096,6 +1112,51 @@ namespace ASC.Files.Core.Data
.ToDictionary(r => r.LeftNode, r => r.RightNode);
}
public IEnumerable<(Folder<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
var q1 = FilesDbContext.Folders
.Where(r => r.TenantId == tenant)
.Where(r => r.FolderType == FolderType.DEFAULT)
.Where(r => r.CreateOn >= from && r.ModifiedOn <= to);
var q2 = FromQuery(q1)
.Select(r => new DbFolderQueryWithSecurity() { DbFolderQuery = r, Security = null });
var q3 = FilesDbContext.Folders
.Where(r => r.TenantId == tenant)
.Where(r => r.FolderType == FolderType.DEFAULT);
var q4 = FromQuery(q3)
.Join(FilesDbContext.Security.DefaultIfEmpty(), r => r.folder.Id.ToString(), s => s.EntryId, (f, s) => new DbFolderQueryWithSecurity { DbFolderQuery = f, Security = s })
.Where(r => r.Security.TenantId == tenant)
.Where(r => r.Security.EntryType == FileEntryType.Folder)
.Where(r => r.Security.Security == FileShare.Restrict)
.Where(r => r.Security.TimeStamp >= from && r.Security.TimeStamp <= to);
return q2.Select(ToFolderWithShare).ToList().Union(q4.Select(ToFolderWithShare).ToList());
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
var q1 = FilesDbContext.Files
.Where(r => r.ModifiedOn > fromTime)
.Select(r => r.TenantId)
.GroupBy(r => r)
.Where(r => r.Count() > 0)
.Select(r => r.Key)
.ToList();
var q2 = FilesDbContext.Security
.Where(r => r.TimeStamp > fromTime)
.Select(r => r.TenantId)
.GroupBy(r => r)
.Where(r => r.Count() > 0)
.Select(r => r.Key)
.ToList();
return q1.Union(q2);
}
private string GetProjectTitle(object folderID)
{
return "";
@ -1159,6 +1220,12 @@ namespace ASC.Files.Core.Data
public bool shared { get; set; }
}
public class DbFolderQueryWithSecurity
{
public DbFolderQuery DbFolderQuery { get; set; }
public DbFilesSecurity Security { get; set; }
}
public static class FolderDaoExtention
{
public static DIHelper AddFolderDaoService(this DIHelper services)

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -538,7 +539,7 @@ namespace ASC.Files.Thirdparty.Box
if (uploadSession.BytesUploaded == uploadSession.BytesTotal)
{
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"),
FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
{
uploadSession.File = SaveFile(uploadSession.File, fs);
}
@ -606,6 +607,16 @@ namespace ASC.Files.Thirdparty.Box
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Core.Files;
using ASC.Web.Studio.Core;
@ -510,6 +511,16 @@ namespace ASC.Files.Thirdparty.Box
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -590,7 +591,7 @@ namespace ASC.Files.Thirdparty.Dropbox
}
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"),
FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
{
return SaveFile(uploadSession.File, fs);
}
@ -653,6 +654,16 @@ namespace ASC.Files.Thirdparty.Dropbox
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Core.Files;
using ASC.Web.Studio.Core;
@ -507,6 +508,16 @@ namespace ASC.Files.Thirdparty.Dropbox
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -582,7 +583,7 @@ namespace ASC.Files.Thirdparty.GoogleDrive
return ToFile(GetDriveEntry(googleDriveSession.FileId));
}
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
{
return SaveFile(uploadSession.File, fs);
}
@ -654,6 +655,16 @@ namespace ASC.Files.Thirdparty.GoogleDrive
return null;
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Core.Files;
using ASC.Web.Studio.Core;
@ -499,6 +500,16 @@ namespace ASC.Files.Thirdparty.GoogleDrive
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -581,7 +582,7 @@ namespace ASC.Files.Thirdparty.OneDrive
return ToFile(GetOneDriveItem(oneDriveSession.FileId));
}
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
{
return SaveFile(uploadSession.File, fs);
}
@ -655,6 +656,16 @@ namespace ASC.Files.Thirdparty.OneDrive
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Core.Files;
using ASC.Web.Studio.Core;
@ -510,6 +511,16 @@ namespace ASC.Files.Thirdparty.OneDrive
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -33,6 +33,7 @@ using ASC.Common;
using ASC.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Files.Services.DocumentService;
@ -509,6 +510,16 @@ namespace ASC.Files.Thirdparty.ProviderDao
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -33,6 +33,7 @@ using ASC.Common;
using ASC.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
namespace ASC.Files.Thirdparty.ProviderDao
@ -411,6 +412,16 @@ filterType, subjectGroup, subjectID, searchText, searchSubfolders, checkShare);
throw new NotImplementedException();
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -484,6 +485,16 @@ namespace ASC.Files.Thirdparty.SharePoint
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -36,6 +36,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Web.Core.Files;
using ASC.Web.Studio.Core;
@ -466,6 +467,16 @@ namespace ASC.Files.Thirdparty.SharePoint
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -41,6 +41,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -261,7 +262,7 @@ namespace ASC.Files.Thirdparty.Sharpbox
{
if (!fileStream.CanSeek)
{
var tempBuffer = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 8096, FileOptions.DeleteOnClose);
var tempBuffer = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, System.IO.FileShare.Read, 8096, FileOptions.DeleteOnClose);
fileStream.CopyTo(tempBuffer);
tempBuffer.Flush();
@ -625,7 +626,7 @@ namespace ASC.Files.Thirdparty.Sharpbox
return ToFile(GetFileById(sharpboxSession.FileId));
}
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose))
using (var fs = new FileStream(uploadSession.GetItemOrDefault<string>("TempPath"), FileMode.Open, FileAccess.Read, System.IO.FileShare.None, 4096, FileOptions.DeleteOnClose))
{
return SaveFile(uploadSession.File, fs);
}
@ -710,6 +711,16 @@ namespace ASC.Files.Thirdparty.Sharpbox
throw new NotImplementedException();
}
public IEnumerable<(File<int>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -41,6 +41,7 @@ using ASC.Core.Common.EF;
using ASC.Core.Tenants;
using ASC.Files.Core;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
@ -530,6 +531,16 @@ namespace ASC.Files.Thirdparty.Sharpbox
return null;
}
public IEnumerable<(Folder<string>, SmallShareRecord)> GetFeeds(int tenant, DateTime from, DateTime to)
{
throw new NotImplementedException();
}
public IEnumerable<string> GetTenantsWithFeeds(DateTime fromTime)
{
throw new NotImplementedException();
}
#endregion
}

View File

@ -9,6 +9,7 @@
<ProjectReference Include="..\..\..\common\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" />
<ProjectReference Include="..\..\..\common\services\ASC.ElasticSearch\ASC.ElasticSearch.csproj" />
<ProjectReference Include="..\..\..\common\services\ASC.Feed.Aggregator\ASC.Feed.Aggregator.csproj" />
<ProjectReference Include="..\Server\ASC.Files.csproj" />
</ItemGroup>

View File

@ -0,0 +1,194 @@

using System;
using System.Collections.Generic;
using System.Linq;
using ASC.Core;
using ASC.Feed;
using ASC.Feed.Data;
using ASC.Files.Core;
using ASC.Files.Core.Security;
using ASC.Web.Core;
using ASC.Web.Core.Files;
using FeedModule = ASC.Feed.Aggregator.Modules.FeedModule;
namespace ASC.Files.Service.Core
{
public class FilesModule : FeedModule
{
private const string fileItem = "file";
private const string sharedFileItem = "sharedFile";
public override string Name => Constants.FilesModule;
public override string Product => "documents";
public override Guid ProductID => WebItemManager.DocumentsProductID;
protected override string DbId => "files";
public IFileDao<int> FileDao { get; }
public IFolderDao<int> FolderDao { get; }
public UserManager UserManager { get; }
public FilesLinkUtility FilesLinkUtility { get; }
public FileSecurity FileSecurity { get; }
public FilesModule(
TenantManager tenantManager,
UserManager userManager,
WebItemSecurity webItemSecurity,
FilesLinkUtility filesLinkUtility,
FileSecurity fileSecurity,
IDaoFactory daoFactory)
: base(tenantManager, webItemSecurity)
{
FileDao = daoFactory.GetFileDao<int>();
FolderDao = daoFactory.GetFolderDao<int>();
UserManager = userManager;
FilesLinkUtility = filesLinkUtility;
FileSecurity = fileSecurity;
}
public override bool VisibleFor(Feed.Aggregator.Feed feed, object data, Guid userId)
{
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return false;
var tuple = ((File<int>, SmallShareRecord))data;
var file = tuple.Item1;
var shareRecord = tuple.Item2;
bool targetCond;
if (feed.Target != null)
{
if (shareRecord != null && shareRecord.ShareBy == userId) return false;
var owner = (Guid)feed.Target;
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
if (!groupUsers.Any())
{
groupUsers.Add(owner);
}
targetCond = groupUsers.Contains(userId);
}
else
{
targetCond = true;
}
return targetCond && FileSecurity.CanRead(file, userId);
}
public override void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
{
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return;
var feed1 = feed.Select(r =>
{
var tuple = ((File<int>, SmallShareRecord))r.Item2;
return new Tuple<FeedRow, File<int>, SmallShareRecord>(r.Item1, tuple.Item1, tuple.Item2);
})
.ToList();
var files = feed1.Where(r => r.Item1.Feed.Target == null).Select(r => r.Item2).ToList();
foreach (var f in feed1.Where(r => r.Item1.Feed.Target != null && !(r.Item3 != null && r.Item3.ShareBy == userId)))
{
var file = f.Item2;
if (IsTarget(f.Item1.Feed.Target, userId) && !files.Any(r => r.UniqID.Equals(file.UniqID)))
{
files.Add(file);
}
}
var canRead = FileSecurity.CanRead(files, userId).Where(r => r.Item2).ToList();
foreach (var f in feed1)
{
if (IsTarget(f.Item1.Feed.Target, userId) && canRead.Any(r => r.Item1.ID.Equals(f.Item2.ID)))
{
f.Item1.Users.Add(userId);
}
}
}
public override IEnumerable<Tuple<Feed.Aggregator.Feed, object>> GetFeeds(FeedFilter filter)
{
var files = FileDao.GetFeeds(filter.Tenant, filter.Time.From, filter.Time.To)
.Where(f => f.Item1.RootFolderType != FolderType.TRASH && f.Item1.RootFolderType != FolderType.BUNCH)
.ToList();
var folderIDs = files.Select(r => r.Item1.FolderID).ToArray();
var folders = FolderDao.GetFolders(folderIDs, checkShare: false);
return files.Select(f => new Tuple<Feed.Aggregator.Feed, object>(ToFeed(f, folders.FirstOrDefault(r => r.ID.Equals(f.Item1.FolderID))), f));
}
public override IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
return FileDao.GetTenantsWithFeeds(fromTime);
}
private Feed.Aggregator.Feed ToFeed((File<int>, SmallShareRecord) tuple, Folder<int> rootFolder)
{
var file = tuple.Item1;
var shareRecord = tuple.Item2;
if (shareRecord != null)
{
var feed = new Feed.Aggregator.Feed(shareRecord.ShareBy, shareRecord.ShareOn, true)
{
Item = sharedFileItem,
ItemId = string.Format("{0}_{1}", file.ID, shareRecord.ShareTo),
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(file.ID, true),
Product = Product,
Module = Name,
Title = file.Title,
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(file.FolderID, false) : string.Empty,
AdditionalInfo = file.ContentLengthString,
Keywords = string.Format("{0}", file.Title),
HasPreview = false,
CanComment = false,
Target = shareRecord.ShareTo,
GroupId = GetGroupId(sharedFileItem, shareRecord.ShareBy, file.FolderID.ToString())
};
return feed;
}
var updated = file.Version != 1;
return new Feed.Aggregator.Feed(file.ModifiedBy, file.ModifiedOn, true)
{
Item = fileItem,
ItemId = string.Format("{0}_{1}", file.ID, file.Version > 1 ? 1 : 0),
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(file.ID, true),
Product = Product,
Module = Name,
Action = updated ? FeedAction.Updated : FeedAction.Created,
Title = file.Title,
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(file.FolderID, false) : string.Empty,
AdditionalInfo = file.ContentLengthString,
Keywords = string.Format("{0}", file.Title),
HasPreview = false,
CanComment = false,
Target = null,
GroupId = GetGroupId(fileItem, file.ModifiedBy, file.FolderID.ToString(), updated ? 1 : 0)
};
}
private bool IsTarget(object target, Guid userId)
{
if (target == null) return true;
var owner = (Guid)target;
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
if (!groupUsers.Any())
{
groupUsers.Add(owner);
}
return groupUsers.Contains(userId);
}
}
}

View File

@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ASC.Core;
using ASC.Feed;
using ASC.Files.Core;
using ASC.Files.Core.Security;
using ASC.Web.Core;
using ASC.Web.Core.Files;
using FeedModule = ASC.Feed.Aggregator.Modules.FeedModule;
namespace ASC.Files.Service.Core
{
public class FoldersModule : FeedModule
{
private const string folderItem = "folder";
private const string sharedFolderItem = "sharedFolder";
protected override string DbId => Constants.FilesDbId;
public override string Name => Constants.FoldersModule;
public override string Product => "documents";
public override Guid ProductID => WebItemManager.DocumentsProductID;
public UserManager UserManager { get; }
public FilesLinkUtility FilesLinkUtility { get; }
public FileSecurity FileSecurity { get; }
public IFileDao<int> FileDao { get; }
public IFolderDao<int> FolderDao { get; }
public FoldersModule(
TenantManager tenantManager,
UserManager userManager,
WebItemSecurity webItemSecurity,
FilesLinkUtility filesLinkUtility,
FileSecurity fileSecurity,
IDaoFactory daoFactory)
: base(tenantManager, webItemSecurity)
{
UserManager = userManager;
FilesLinkUtility = filesLinkUtility;
FileSecurity = fileSecurity;
FileDao = daoFactory.GetFileDao<int>();
FolderDao = daoFactory.GetFolderDao<int>();
}
public override bool VisibleFor(Feed.Aggregator.Feed feed, object data, Guid userId)
{
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return false;
var tuple = (Tuple<Folder<int>, SmallShareRecord>)data;
var folder = tuple.Item1;
var shareRecord = tuple.Item2;
bool targetCond;
if (feed.Target != null)
{
if (shareRecord != null && shareRecord.ShareBy == userId) return false;
var owner = (Guid)feed.Target;
var groupUsers = UserManager.GetUsersByGroup(owner).Select(x => x.ID).ToList();
if (!groupUsers.Any())
{
groupUsers.Add(owner);
}
targetCond = groupUsers.Contains(userId);
}
else
{
targetCond = true;
}
return targetCond && FileSecurity.CanRead(folder, userId);
}
public override IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime)
{
return FolderDao.GetTenantsWithFeeds(fromTime);
}
public override IEnumerable<Tuple<Feed.Aggregator.Feed, object>> GetFeeds(FeedFilter filter)
{
var folders = FolderDao.GetFeeds(filter.Tenant, filter.Time.From, filter.Time.To)
.Where(f => f.Item1.RootFolderType != FolderType.TRASH && f.Item1.RootFolderType != FolderType.BUNCH)
.ToList();
var parentFolderIDs = folders.Select(r => r.Item1.ParentFolderID).ToArray();
var parentFolders = FolderDao.GetFolders(parentFolderIDs, checkShare: false);
return folders.Select(f => new Tuple<Feed.Aggregator.Feed, object>(ToFeed(f, parentFolders.FirstOrDefault(r => r.ID.Equals(f.Item1.ParentFolderID))), f));
}
private Feed.Aggregator.Feed ToFeed((Folder<int>, SmallShareRecord) tuple, Folder<int> rootFolder)
{
var folder = tuple.Item1;
var shareRecord = tuple.Item2;
if (shareRecord != null)
{
var feed = new Feed.Aggregator.Feed(shareRecord.ShareBy, shareRecord.ShareOn, true)
{
Item = sharedFolderItem,
ItemId = string.Format("{0}_{1}", folder.ID, shareRecord.ShareTo),
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ID, false),
Product = Product,
Module = Name,
Title = folder.Title,
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ParentFolderID, false) : string.Empty,
Keywords = string.Format("{0}", folder.Title),
HasPreview = false,
CanComment = false,
Target = shareRecord.ShareTo,
GroupId = GetGroupId(sharedFolderItem, shareRecord.ShareBy, folder.ParentFolderID.ToString())
};
return feed;
}
return new Feed.Aggregator.Feed(folder.CreateBy, folder.CreateOn)
{
Item = folderItem,
ItemId = folder.ID.ToString(),
ItemUrl = FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ID, false),
Product = Product,
Module = Name,
Title = folder.Title,
ExtraLocation = rootFolder.FolderType == FolderType.DEFAULT ? rootFolder.Title : string.Empty,
ExtraLocationUrl = rootFolder.FolderType == FolderType.DEFAULT ? FilesLinkUtility.GetFileRedirectPreviewUrl(folder.ParentFolderID, false) : string.Empty,
Keywords = string.Format("{0}", folder.Title),
HasPreview = false,
CanComment = false,
Target = null,
GroupId = GetGroupId(folderItem, folder.CreateBy, folder.ParentFolderID.ToString())
};
}
}
}

View File

@ -7,6 +7,7 @@ using ASC.Common.Caching;
using ASC.Common.DependencyInjection;
using ASC.Common.Logging;
using ASC.ElasticSearch;
using ASC.Feed.Aggregator;
using ASC.Web.Files.Core.Search;
using ASC.Web.Files.Utils;
@ -39,6 +40,7 @@ namespace ASC.Files.Service
)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env}.json", true)
.AddJsonFile($"appsettings.services.json", true)
.AddJsonFile("storage.json")
.AddJsonFile("notify.json")
.AddJsonFile("kafka.json")
@ -58,8 +60,12 @@ namespace ASC.Files.Service
.AddFactoryIndexerFileService()
.AddFactoryIndexerFolderService();
var a = typeof(FactoryIndexer<ISearchItem>).ToString();
services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath, false, false, "search.json");
diHelper.AddNLogManager("ASC.Feed.Agregator");
services.AddHostedService<FeedAggregatorService>();
diHelper
.AddFeedAggregatorService();
services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath, true, false, "search.json", "feed.json");
})
.UseConsoleLifetime()
.Build();

View File

@ -0,0 +1,22 @@
{
"components": [
{
"type": "ASC.Files.Service.Core.FilesModule, ASC.Files.Service",
"services": [
{
"type": "ASC.Feed.Aggregator.Modules.IFeedModule, ASC.Feed.Aggregator"
}
],
"instanceScope": "perlifetimescope"
},
{
"type": "ASC.Files.Service.Core.FoldersModule, ASC.Files.Service",
"services": [
{
"type": "ASC.Feed.Aggregator.Modules.IFeedModule, ASC.Feed.Aggregator"
}
],
"instanceScope": "perlifetimescope"
}
]
}