Merge branch 'master' into feature/files

This commit is contained in:
Nikita Gopienko 2020-05-14 17:08:59 +03:00
commit de55454c84
94 changed files with 1345 additions and 1484 deletions

View File

@ -50,6 +50,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Files", "products\ASC.F
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppLimit.CloudComputing.SharpBox", "thirdparty\AppLimit.CloudComputing.SharpBox\AppLimit.CloudComputing.SharpBox.csproj", "{5B53855C-4347-4402-B750-76C6295A35D3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Files.Service", "products\ASC.Files\Service\ASC.Files.Service.csproj", "{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -144,6 +146,10 @@ Global
{5B53855C-4347-4402-B750-76C6295A35D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B53855C-4347-4402-B750-76C6295A35D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B53855C-4347-4402-B750-76C6295A35D3}.Release|Any CPU.Build.0 = Release|Any CPU
{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D41FFFF-816C-40B2-95CD-E2DDDCB83784}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,2 @@
echo "RUN ASC.Files"
call dotnet run --project ..\..\products\ASC.Files\Service\ASC.Files.Service.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=files

View File

@ -12,8 +12,6 @@ using Confluent.Kafka;
using Google.Protobuf;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Common.Caching

View File

@ -26,7 +26,7 @@ namespace ASC.Common.DependencyInjection
public static class AutofacExtension
{
public static IContainer AddAutofac(this IServiceCollection services, IConfiguration configuration, string currentDir)
public static IContainer AddAutofac(this IServiceCollection services, IConfiguration configuration, string currentDir, params string[] intern)
{
var folder = configuration["core:products:folder"];
var subfolder = configuration["core:products:subfolder"];
@ -42,19 +42,32 @@ namespace ASC.Common.DependencyInjection
}
var builder = new ContainerBuilder();
var modules = new string[] { "autofac.json", "autofac.products.json", "autofac.consumers.json" };
var modules = new List<(bool, string)>
{
(true, "autofac.json"),
(true, "autofac.products.json"),
(true, "autofac.consumers.json")
};
if (intern != null)
{
modules.AddRange(intern.Select(r => (false, r)));
}
foreach (var p in modules)
{
var config = new ConfigurationBuilder()
.SetBasePath(configuration["pathToConf"])
.AddJsonFile(p);
var config = new ConfigurationBuilder();
if (p.Item1)
{
config.SetBasePath(configuration["pathToConf"]);
}
config.AddJsonFile(p.Item2);
var root = config.Build();
var module = new ConfigurationModule(root);
builder.RegisterModule(module);
if (p == "autofac.products.json")
if (p.Item2 == "autofac.products.json")
{
FindAndLoad(root.GetSection("components"));
}

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

View File

@ -35,7 +35,6 @@ using log4net.Config;
using log4net.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NLog;

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Microsoft.Extensions.Configuration;
namespace ASC.Common.Utils

View File

@ -30,8 +30,6 @@ using System.Text;
using ASC.Security.Cryptography;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;
namespace ASC.Common.Utils

View File

@ -35,7 +35,6 @@ using ASC.Core.Data;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Caching

View File

@ -33,8 +33,6 @@ using ASC.Common.Utils;
using ASC.Core.Common.EF.Context;
using ASC.Core.Data;
using ASC.Core.Tenants;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Caching

View File

@ -36,8 +36,6 @@ using ASC.Core.Common.EF;
using ASC.Core.Data;
using ASC.Core.Tenants;
using ASC.Core.Users;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Caching

View File

@ -377,6 +377,11 @@ namespace ASC.Core.Common.Configuration
public ConsumerFactory(IContainer builder)
{
Builder = builder.BeginLifetimeScope();
}
public ConsumerFactory(ILifetimeScope builder)
{
Builder = builder;
}
public Consumer GetByKey(string key)

View File

@ -389,7 +389,8 @@ namespace ASC.Core
public static DIHelper AddCoreConfigurationService(this DIHelper services)
{
services.TryAddScoped<CoreConfiguration>();
return services
return services
.AddTenantManagerService()
.AddCoreSettingsService();
}
}

View File

@ -128,7 +128,6 @@ namespace ASC.Core
ITenantService tenantService,
IQuotaService quotaService,
ITariffService tariffService,
IHttpContextAccessor httpContextAccessor,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings)
{
@ -137,6 +136,16 @@ namespace ASC.Core
TariffService = tariffService;
CoreBaseSettings = coreBaseSettings;
CoreSettings = coreSettings;
}
public TenantManager(
ITenantService tenantService,
IQuotaService quotaService,
ITariffService tariffService,
IHttpContextAccessor httpContextAccessor,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings) : this(tenantService, quotaService, tariffService, coreBaseSettings, coreSettings)
{
HttpContext = httpContextAccessor?.HttpContext;
}

View File

@ -76,7 +76,6 @@ namespace ASC.Core
private IHttpContextAccessor HttpContextAccessor { get; }
public SecurityContext(
IHttpContextAccessor httpContextAccessor,
UserManager userManager,
AuthManager authentication,
AuthContext authContext,
@ -95,6 +94,20 @@ namespace ASC.Core
UserFormatter = userFormatter;
CookieStorage = cookieStorage;
TenantCookieSettingsHelper = tenantCookieSettingsHelper;
}
public SecurityContext(
IHttpContextAccessor httpContextAccessor,
UserManager userManager,
AuthManager authentication,
AuthContext authContext,
TenantManager tenantManager,
UserFormatter userFormatter,
CookieStorage cookieStorage,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
IOptionsMonitor<ILog> options
) : this(userManager, authentication, authContext, tenantManager, userFormatter, cookieStorage, tenantCookieSettingsHelper, options)
{
HttpContextAccessor = httpContextAccessor;
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic;
using ASC.Common;
using ASC.Common.Logging;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Common.EF
@ -79,7 +79,7 @@ namespace ASC.Core.Common.EF
services.TryAddScoped<IConfigureOptions<MultiRegionalDbContext<T>>, ConfigureMultiRegionalDbContext<T>>();
//services.TryAddScoped<T>();
return services;
return services.AddLoggerService();
}
}
}

View File

@ -28,11 +28,7 @@ namespace ASC.Core.Common.EF.Context
modelBuilder.AddCoreSettings();
modelBuilder.Entity<DbTenant>()
.HasOne(r => r.Partner)
.WithOne(r => r.Tenant)
.HasForeignKey<DbTenantPartner>(r => new { r.TenantId })
.HasPrincipalKey<DbTenant>(r => new { r.Id });
modelBuilder.AddDbTenant();
}
}

View File

@ -5,7 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace ASC.Core.Common.EF.Model
{
[Table("webstudio_index")]
public class DbWebstudioIndex
public class DbWebstudioIndex : BaseEntity
{
[Key]
[Column("index_name")]
@ -13,5 +13,7 @@ namespace ASC.Core.Common.EF.Model
[Column("last_modified")]
public DateTime LastModified { get; set; }
public override object[] GetKeys() => new[] { IndexName };
}
}

View File

@ -1,7 +1,10 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using ASC.Core.Tenants;
using Microsoft.EntityFrameworkCore;
namespace ASC.Core.Common.EF.Model
{
[Table("tenants_tenants")]
@ -39,4 +42,16 @@ namespace ASC.Core.Common.EF.Model
public DbTenantPartner Partner { get; set; }
}
public static class DbTenantExtension
{
public static void AddDbTenant(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<DbTenant>()
.HasOne(r => r.Partner)
.WithOne(r => r.Tenant)
.HasForeignKey<DbTenantPartner>(r => new { r.TenantId })
.HasPrincipalKey<DbTenant>(r => new { r.Id });
}
}
}

View File

@ -31,8 +31,6 @@ using System.Linq;
using ASC.Common;
using ASC.Core.Users;
using ASC.Notify.Recipients;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Notify
{

View File

@ -48,17 +48,25 @@ namespace ASC.Core.Security.Authentication
private ILog Log { get; }
public CookieStorage(
IHttpContextAccessor httpContextAccessor,
InstanceCrypto instanceCrypto,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
IOptionsMonitor<ILog> options)
{
InstanceCrypto = instanceCrypto;
TenantCookieSettingsHelper = tenantCookieSettingsHelper;
HttpContext = httpContextAccessor.HttpContext;
Log = options.CurrentValue;
}
public CookieStorage(
IHttpContextAccessor httpContextAccessor,
InstanceCrypto instanceCrypto,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
IOptionsMonitor<ILog> options)
: this(instanceCrypto, tenantCookieSettingsHelper, options)
{
HttpContext = httpContextAccessor.HttpContext;
}
public bool DecryptCookie(string cookie, out int tenant, out Guid userid, out string login, out string password, out int indexTenant, out DateTime expire, out int indexUser)
{
tenant = Tenant.DEFAULT_TENANT;

View File

@ -27,8 +27,6 @@
using System;
using System.Collections.Generic;
using ASC.Core.Security.Authorizing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Common.Security.Authorizing
{

View File

@ -31,8 +31,6 @@ using System.Linq;
using ASC.Common;
using ASC.Common.Security;
using ASC.Common.Security.Authorizing;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Security.Authorizing
{

View File

@ -33,8 +33,6 @@ using ASC.Common.Security;
using ASC.Common.Security.Authentication;
using ASC.Common.Security.Authorizing;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Constants = ASC.Core.Configuration.Constants;
namespace ASC.Core.Security.Authorizing

View File

@ -35,7 +35,6 @@ using ASC.Common.Security.Authorizing;
using ASC.Core.Users;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Security.Authorizing
{

View File

@ -57,7 +57,9 @@ namespace ASC.Core.Common.Settings
{
services.TryAddScoped<SettingsManager>();
return services.AddDbSettingsManagerService();
return services
.AddAuthContextService()
.AddDbSettingsManagerService();
}
}
}

View File

@ -33,7 +33,6 @@ using ASC.Common.Threading.Progress;
using ASC.Core.Users;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Data.Reassigns

View File

@ -7,7 +7,6 @@ using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Data.Storage.Configuration
{

View File

@ -133,7 +133,6 @@ namespace ASC.Data.Storage.Configuration
IOptionsMonitor<ILog> options,
TenantManager tenantManager,
SettingsManager settingsManager,
IHttpContextAccessor httpContextAccessor,
ConsumerFactory consumerFactory)
{
BaseStorageSettingsListener = baseStorageSettingsListener;
@ -144,9 +143,23 @@ namespace ASC.Data.Storage.Configuration
Options = options;
TenantManager = tenantManager;
SettingsManager = settingsManager;
HttpContextAccessor = httpContextAccessor;
ConsumerFactory = consumerFactory;
}
public StorageSettingsHelper(
BaseStorageSettingsListener baseStorageSettingsListener,
StorageFactoryConfig storageFactoryConfig,
PathUtils pathUtils,
EmailValidationKeyProvider emailValidationKeyProvider,
ICacheNotify<DataStoreCacheItem> cache,
IOptionsMonitor<ILog> options,
TenantManager tenantManager,
SettingsManager settingsManager,
IHttpContextAccessor httpContextAccessor,
ConsumerFactory consumerFactory)
: this(baseStorageSettingsListener, storageFactoryConfig, pathUtils, emailValidationKeyProvider, cache, options, tenantManager, settingsManager, consumerFactory)
{
HttpContextAccessor = httpContextAccessor;
}
public bool Save<T>(BaseStorageSettings<T> baseStorageSettings) where T : class, ISettings, new()
{

View File

@ -159,7 +159,6 @@ namespace ASC.Data.Storage
StaticUploader staticUploader,
SettingsManager settingsManager,
StorageSettingsHelper storageSettingsHelper,
IHttpContextAccessor httpContextAccessor,
IHostEnvironment hostEnvironment,
CoreBaseSettings coreBaseSettings,
IOptionsMonitor<ILog> options)
@ -168,12 +167,25 @@ namespace ASC.Data.Storage
StaticUploader = staticUploader;
SettingsManager = settingsManager;
StorageSettingsHelper = storageSettingsHelper;
HttpContextAccessor = httpContextAccessor;
HostEnvironment = hostEnvironment;
CoreBaseSettings = coreBaseSettings;
Options = options;
}
public WebPath(
WebPathSettings webPathSettings,
StaticUploader staticUploader,
SettingsManager settingsManager,
StorageSettingsHelper storageSettingsHelper,
IHttpContextAccessor httpContextAccessor,
IHostEnvironment hostEnvironment,
CoreBaseSettings coreBaseSettings,
IOptionsMonitor<ILog> options)
: this(webPathSettings, staticUploader, settingsManager, storageSettingsHelper, hostEnvironment, coreBaseSettings, options)
{
HttpContextAccessor = httpContextAccessor;
}
public string GetPath(string relativePath)
{
if (!string.IsNullOrEmpty(relativePath) && relativePath.IndexOf('~') == 0)
@ -194,7 +206,7 @@ namespace ASC.Data.Storage
}
}
return WebPathSettings.GetPath(HttpContextAccessor.HttpContext, Options, relativePath);
return WebPathSettings.GetPath(HttpContextAccessor?.HttpContext, Options, relativePath);
}
public bool Exists(string relativePath)

View File

@ -48,7 +48,6 @@ namespace ASC.MessagingSystem
public MessageService(
IConfiguration configuration,
IHttpContextAccessor httpContextAccessor,
MessageFactory messageFactory,
DbMessageSender sender,
MessagePolicy messagePolicy,
@ -61,11 +60,22 @@ namespace ASC.MessagingSystem
this.sender = sender;
MessagePolicy = messagePolicy;
request = httpContextAccessor?.HttpContext?.Request;
MessageFactory = messageFactory;
log = options.CurrentValue;
}
public MessageService(
IConfiguration configuration,
IHttpContextAccessor httpContextAccessor,
MessageFactory messageFactory,
DbMessageSender sender,
MessagePolicy messagePolicy,
IOptionsMonitor<ILog> options)
: this(configuration, messageFactory, sender, messagePolicy, options)
{
request = httpContextAccessor?.HttpContext?.Request;
}
#region HttpRequest
public void Send(MessageAction action)

View File

@ -22,12 +22,12 @@
<None Remove="protos\SearchItem.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Elasticsearch.Net" Version="6.5.0" />
<PackageReference Include="Elasticsearch.Net" Version="7.6.1" />
<PackageReference Include="Grpc.Tools" Version="2.27.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NEST" Version="6.5.0" />
<PackageReference Include="NEST" Version="7.6.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" />

View File

@ -28,90 +28,6 @@ using System;
namespace ASC.ElasticSearch
{
[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute : Attribute
{
public string ColumnName { get; private set; }
public ColumnTypeEnum ColumnType { get; private set; }
public int Order { get; private set; }
public Analyzer Analyzer { get; set; }
public Filter Filter { get; set; }
public CharFilter CharFilter { get; set; }
public ColumnAttribute(string columnName, int order, ColumnTypeEnum columnType = ColumnTypeEnum.Content, Filter filter = Filter.lowercase, CharFilter charFilter = CharFilter.io)
{
ColumnName = columnName;
ColumnType = columnType;
Order = order;
Analyzer = Analyzer.whitespace;
Filter = filter;
CharFilter = charFilter;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ColumnIdAttribute : ColumnAttribute
{
public ColumnIdAttribute(string columnName)
: base(columnName, -3, ColumnTypeEnum.Id)
{
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ColumnTenantIdAttribute : ColumnAttribute
{
public ColumnTenantIdAttribute(string columnName)
: base(columnName, -2, ColumnTypeEnum.TenantId)
{
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ColumnLastModifiedAttribute : ColumnAttribute
{
public ColumnLastModifiedAttribute(string columnName)
: base(columnName, -1, ColumnTypeEnum.LastModified)
{
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ColumnConditionAttribute : ColumnAttribute
{
internal object Value { get; private set; }
public ColumnConditionAttribute(string columnName, int order, object value = null)
: base(columnName, order, ColumnTypeEnum.Condition)
{
Value = value;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ColumnMeta : ColumnAttribute
{
public ColumnMeta(string columnName, int order)
: base(columnName, order, ColumnTypeEnum.Meta)
{
}
}
public enum ColumnTypeEnum
{
Id,
TenantId,
LastModified,
Content,
Condition,
Meta
}
public enum Analyzer
{
standard,

View File

@ -1,58 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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;
namespace ASC.ElasticSearch
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class JoinAttribute : Attribute
{
public JoinTypeEnum JoinType { get; private set; }
public string[] ColumnsFrom { get; private set; }
public string[] ColumnsTo { get; private set; }
public JoinAttribute(JoinTypeEnum joinType, params string[] columns)
{
JoinType = joinType;
ColumnsFrom = new string[columns.Length];
ColumnsTo = new string[columns.Length];
for (var i = 0; i< columns.Length; i++)
{
var column = columns[i].Split(':');
ColumnsFrom[i] = column[0];
ColumnsTo[i] = column[1];
}
}
}
public enum JoinTypeEnum
{
Inner,
Sub
}
}

View File

@ -1,64 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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.Configuration;
namespace ASC.ElasticSearch.Config
{
class ElasticSection : ConfigurationSection
{
[ConfigurationProperty("host", IsRequired = true, DefaultValue = "localhost")]
public string Host
{
get { return (string)this["host"]; }
}
[ConfigurationProperty("port", IsRequired = false, DefaultValue = "9200")]
public int Port
{
get { return Convert.ToInt32(this["port"]); }
}
[ConfigurationProperty("scheme", IsRequired = false, DefaultValue = "http")]
public string Scheme
{
get { return (string)this["scheme"]; }
}
[ConfigurationProperty("period", IsRequired = false, DefaultValue = 1)]
public int Period
{
get { return Convert.ToInt32(this["period"]); }
}
[ConfigurationProperty("memoryLimit", IsRequired = false, DefaultValue = 10 * 1024 * 1024L)]
public long MemoryLimit
{
get { return Convert.ToInt64(this["memoryLimit"]); }
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Linq.Expressions;
namespace ASC.ElasticSearch
{
public interface ISearchItem
{
public int Id { get; set; }
public int TenantId { get; set; }
public string IndexName { get; }
public Expression<Func<ISearchItem, object[]>> SearchContentFields { get; }
}
public interface ISearchItemDocument : ISearchItem
{
public Document Document { get; set; }
}
}

View File

@ -30,6 +30,7 @@ using System.Linq;
using System.Runtime.Serialization;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Core;
using ASC.Core.Common.Settings;
@ -82,21 +83,27 @@ namespace ASC.ElasticSearch.Core
}
public class SearchSettingsHelper
{
{
public TenantManager TenantManager { get; }
public SettingsManager SettingsManager { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public FactoryIndexer FactoryIndexer { get; }
public FactoryIndexer FactoryIndexer { get; }
public ICacheNotify<ReIndexAction> CacheNotify { get; }
public IServiceProvider ServiceProvider { get; }
public SearchSettingsHelper(
public SearchSettingsHelper(
TenantManager tenantManager,
SettingsManager settingsManager,
CoreBaseSettings coreBaseSettings,
FactoryIndexer factoryIndexer,
FactoryIndexer factoryIndexer,
ICacheNotify<ReIndexAction> cacheNotify,
IServiceProvider serviceProvider)
{
{
TenantManager = tenantManager;
SettingsManager = settingsManager;
CoreBaseSettings = coreBaseSettings;
FactoryIndexer = factoryIndexer;
FactoryIndexer = factoryIndexer;
CacheNotify = cacheNotify;
ServiceProvider = serviceProvider;
}
@ -114,14 +121,12 @@ namespace ASC.ElasticSearch.Core
}).ToList();
}
private List<WrapperWithDoc> allItems;
internal List<WrapperWithDoc> AllItems
private List<IFactoryIndexer> allItems;
internal List<IFactoryIndexer> AllItems
{
get
{
return allItems ?? (allItems = FactoryIndexer.Builder.Resolve<IEnumerable<Wrapper>>()
.OfType<WrapperWithDoc>()
.ToList());
return allItems ?? (allItems = FactoryIndexer.Builder.Resolve<IEnumerable<IFactoryIndexer>>().ToList());
}
}
@ -137,19 +142,18 @@ namespace ASC.ElasticSearch.Core
settings.Items = items;
settings.Data = JsonConvert.SerializeObject(items);
SettingsManager.Save(settings);
//TODO:
//using (var service = new ServiceClient())
//{
// service.ReIndex(toReIndex.Select(r => r.ID).ToList(), TenantManager.GetCurrentTenant().TenantId);
//}
var action = new ReIndexAction() { Tenant = TenantManager.GetCurrentTenant().TenantId };
action.Names.AddRange(toReIndex.Select(r => r.ID).ToList());
CacheNotify.Publish(action, CacheNotifyAction.Any);
}
public bool CanSearchByContent<T>(int tenantId) where T : Wrapper
public bool CanSearchByContent<T>(int tenantId) where T : class, ISearchItem
{
if (!SearchByContentEnabled) return false;
if (!typeof(T).IsSubclassOf(typeof(WrapperWithDoc)))
if (typeof(ISearchItemDocument).IsAssignableFrom(typeof(T)))
{
return false;
}
@ -190,7 +194,8 @@ namespace ASC.ElasticSearch.Core
return services
.AddSettingsManagerService()
.AddCoreBaseSettingsService()
.AddFactoryIndexerService();
.AddFactoryIndexerService()
.AddTenantManagerService();
}
}
}

View File

@ -29,13 +29,13 @@ using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Nest;
using Nest;
namespace ASC.ElasticSearch
{
public class Selector<T> where T : Wrapper
public class Selector<T> where T : class, ISearchItem
{
private readonly QueryContainerDescriptor<T> queryContainerDescriptor = new QueryContainerDescriptor<T>();
private SortDescriptor<T> sortContainerDescriptor = new SortDescriptor<T>();
@ -120,11 +120,11 @@ namespace ASC.ElasticSearch
if (IsExactlyPhrase(value))
{
queryContainer = queryContainer & Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value.TrimQuotes())));
queryContainer &= Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value.TrimQuotes())));
}
else if (value.HasOtherLetter() || IsExactly(value))
{
queryContainer = queryContainer & Wrap(selector, (a, w) => w.Match(r => r.Field(a).Query(value.TrimQuotes())));
queryContainer &= Wrap(selector, (a, w) => w.Match(r => r.Field(a).Query(value.TrimQuotes())));
}
else
{
@ -134,19 +134,19 @@ namespace ASC.ElasticSearch
foreach (var p in phrase)
{
var p1 = p;
queryContainer = queryContainer & Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(p1.WrapAsterisk())));
queryContainer &= Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(p1.WrapAsterisk())));
}
}
else
{
queryContainer = queryContainer & Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(value.WrapAsterisk())));
queryContainer &= Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(value.WrapAsterisk())));
}
}
if (IsExactly(value))
{
queryContainer = queryContainer | Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value)));
queryContainer |= Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value)));
}
return this;
@ -199,7 +199,7 @@ namespace ASC.ElasticSearch
public Selector<T> MatchAll(string value)
{
Match(() => ServiceProvider.GetService<T>().GetContentProperties(), value);
Match(() => ((NewArrayExpression)(ServiceProvider.GetService<T>().SearchContentFields).Body).Expressions.ToArray(), value);
return this;
}
@ -331,7 +331,6 @@ namespace ASC.ElasticSearch
if (string.IsNullOrEmpty(path) &&
!string.IsNullOrEmpty(fieldSelector.Name) &&
fieldSelector.Name.StartsWith(JoinTypeEnum.Sub + ":") &&
fieldSelector.Name.IndexOf(".", StringComparison.InvariantCulture) > 0)
{
var splitted = fieldSelector.Name.Split(':')[1];

View File

@ -1,370 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ASC.ElasticSearch.Core;
using Nest;
using Newtonsoft.Json.Linq;
namespace ASC.ElasticSearch
{
public abstract class Wrapper
{
protected internal abstract string Table { get; }
protected internal virtual string IndexName
{
get { return Table; }
}
[ColumnId("id")]
public virtual int Id { get; set; }
[ColumnTenantId("tenant_id")]
public virtual int TenantId { get; set; }
[ColumnLastModified("last_modified_on"), Date]
public virtual DateTime LastModifiedOn { get; set; }
private List<PropertyInfo> orderedProperties;
private List<PropertyInfo> OrderedProperties
{
get
{
return orderedProperties ?? (orderedProperties = GetType()
.GetProperties()
.Where(r =>
{
var column = r.GetCustomAttribute<ColumnAttribute>();
return column != null && !string.IsNullOrEmpty(column.ColumnName);
})
.OrderBy(r => r.GetCustomAttribute<ColumnAttribute>().Order)
.ToList());
}
}
private List<ColumnAttribute> columns;
private List<ColumnAttribute> Columns
{
get
{
return columns ?? (columns = OrderedProperties
.Select(r => r.GetCustomAttribute<ColumnAttribute>())
.ToList());
}
}
internal Converter<object[], Wrapper> GetDataConverter(bool sub = false)
{
return data =>
{
var result = Activator.CreateInstance(GetType());
var i = 0;
var props = OrderedProperties;
for (; i < props.Count; i++)
{
if (data[i] == null) continue;
object newValue;
if (props[i].PropertyType == typeof(Guid))
{
newValue = Guid.Parse(data[i].ToString());
}
else if (props[i].PropertyType == typeof(bool))
{
try
{
newValue = Convert.ToBoolean(data[i]);
}
catch (Exception)
{
newValue = data[i] != null;
}
}
else
{
try
{
newValue = Convert.ChangeType(data[i], props[i].PropertyType);
}
catch (Exception)
{
newValue = Activator.CreateInstance(props[i].PropertyType);
}
}
props[i].SetValue(result, newValue);
}
var joins = GetType()
.GetProperties()
.Where(r => r.GetCustomAttribute<JoinAttribute>() != null)
.ToList();
if (!joins.Any()) return (Wrapper) result;
data = data.Skip(i).ToArray();
foreach (var join in joins)
{
Wrapper joinWrapper;
if (join.PropertyType.IsGenericType)
{
joinWrapper = Activator.CreateInstance(join.PropertyType.GenericTypeArguments[0]) as Wrapper;
}
else
{
joinWrapper = Activator.CreateInstance(join.PropertyType) as Wrapper;
}
if (joinWrapper == null) continue;
var joinAttr = join.GetCustomAttribute<JoinAttribute>();
var joinSub = sub || joinAttr.JoinType == JoinTypeEnum.Sub;
List<object[]> newArray;
if (joinSub && join.PropertyType.IsGenericType)
{
newArray = data[0] == null
? new List<object[]>()
: JArray.Parse(data[0].ToString()).Select(r => ((JArray)r).Select(q => (object)q).ToArray()).ToList();
}
else
{
newArray = new List<object[]> {data};
}
var newArrayValue = newArray.ConvertAll(joinWrapper.GetDataConverter(joinSub));
object newValue;
if (joinSub && join.PropertyType.IsGenericType)
{
var list = (IList)Activator.CreateInstance(join.PropertyType);
foreach (var item in newArrayValue)
{
list.Add(item);
}
newValue = list;
}
else
{
newValue = Convert.ChangeType(newArrayValue.First(), join.PropertyType);
}
join.SetValue(result, newValue);
var skipCount = joinSub
? 1
: joinWrapper.OrderedProperties.Count;
data = data.Skip(skipCount).ToArray();
}
return (Wrapper) result;
};
}
internal string GetColumnName(ColumnTypeEnum columnType, string alias)
{
var column = Columns.FirstOrDefault(r => r.ColumnType == columnType);
if (column == null || string.IsNullOrEmpty(column.ColumnName)) return null;
return alias + "." + column.ColumnName;
}
internal bool TryGetColumnName(ColumnTypeEnum columnType, string alias, out string columnName)
{
columnName = GetColumnName(columnType, alias);
return columnName != null;
}
internal bool TryGetColumnNames(List<ColumnTypeEnum> columnType, string alias, out List<string> columnName)
{
columnName = new List<string>();
foreach (var cType in columnType)
{
var type = cType;
var column = Columns.Where(r => r.ColumnType == type);
columnName.AddRange(column.Select(r => alias + "." + r.ColumnName));
}
return columnName.Any();
}
internal string[] GetColumnNames(string alias)
{
return Columns
.Select(r => alias + "." + r.ColumnName)
.ToArray();
}
internal string[] GetContentProperties()
{
var result = OrderedProperties
.Where(r => r.GetCustomAttribute<ColumnAttribute>().ColumnType == ColumnTypeEnum.Content)
.Select(r => ToLowerCamelCase(r.Name))
.ToList();
if (this is WrapperWithDoc)
{
result.Add("document.attachment.content");
}
foreach (var join in GetJoins())
{
var joinType = join.Value.Name;
var joinAttr = join.Value.GetCustomAttribute<JoinAttribute>().JoinType;
result.AddRange(join.Key.GetContentProperties()
.Select(r => (joinAttr == JoinTypeEnum.Sub ? joinAttr + ":" : "") + ToLowerCamelCase(joinType) + "." + r));
}
return result.ToArray();
}
internal Dictionary<Wrapper, PropertyInfo> GetJoins()
{
var result = new Dictionary<Wrapper, PropertyInfo>();
var joins = GetType()
.GetProperties()
.Where(r => r.GetCustomAttribute<JoinAttribute>() != null)
.ToList();
foreach (var join in joins)
{
Wrapper joinWrapper;
if (join.PropertyType.IsGenericType)
{
joinWrapper = Activator.CreateInstance(join.PropertyType.GenericTypeArguments[0]) as Wrapper;
}
else
{
joinWrapper = Activator.CreateInstance(join.PropertyType) as Wrapper;
}
if (joinWrapper == null) continue;
result.Add(joinWrapper, join);
}
return result;
}
internal Dictionary<string, object> GetConditions(string alias)
{
var result = new Dictionary<string, object>();
var conditions = Columns.OfType<ColumnConditionAttribute>();
foreach (var con in conditions.Where(r=> r.Value != null))
{
result.Add(alias + "." + con.ColumnName, con.Value);
}
return result;
}
internal string ToLowerCamelCase(string value)
{
return char.ToLowerInvariant(value[0]) + value.Substring(1);
}
internal Func<PropertiesDescriptor<T>, IPromise<IProperties>> GetProperties<T>() where T : Wrapper
{
var analyzers = GetAnalyzers();
return p =>
{
foreach (var c in analyzers)
{
var c1 = c;
string analyzer;
if (c.Value.CharFilter != CharFilter.io)
{
analyzer = c.Key;
}
else
{
analyzer = c1.Value.Analyzer + "custom";
}
p.Text(s => s.Name(c1.Key).Analyzer(analyzer));
}
if (this is WrapperWithDoc)
{
p.Object<Document>(
r => r.Name("document").Properties(
q => q.Object<Attachment>(
a => a.Name("attachment").Properties(
w => w.Text(
t => t.Name("content").Analyzer("document"))))));
}
return p;
};
}
internal Dictionary<string, ColumnAttribute> GetAnalyzers()
{
var result = new Dictionary<string, ColumnAttribute>();
foreach (var prop in OrderedProperties)
{
var column = prop.GetCustomAttribute<ColumnAttribute>();
if (column == null || column.ColumnType != ColumnTypeEnum.Content || column.Analyzer == Analyzer.standard) continue;
result.Add(prop.Name.ToLowerInvariant()[0] + prop.Name.Substring(1), column);
}
return result;
}
internal Dictionary<Type, string> GetNested()
{
var result = new Dictionary<Type, string>();
var joins = GetType().GetProperties().Where(r =>
{
var attr = r.GetCustomAttribute<JoinAttribute>();
return attr != null;
});
foreach (var prop in joins)
{
result.Add(prop.PropertyType, prop.Name);
}
return result;
}
}
}

View File

@ -1,81 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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.IO;
using System.Text;
using ASC.Common.Logging;
using Nest;
using Newtonsoft.Json;
namespace ASC.ElasticSearch.Core
{
public abstract class WrapperWithDoc : Wrapper
{
public Document Document { get; set; }
public const long MaxContentLength = 2 * 1024 * 1024 * 1024L;
protected abstract Stream GetDocumentStream();
[Ignore, JsonIgnore]
public abstract string SettingsTitle { get; }
internal void InitDocument(bool index, ILog log)
{
Document = new Document
{
Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(""))
};
try
{
if (!index) return;
using (var stream = GetDocumentStream())
{
if (stream == null) return;
Document = new Document
{
Data = Convert.ToBase64String(stream.GetCorrectBuffer())
};
}
}
catch (FileNotFoundException e)
{
log.Error("InitDocument FileNotFoundException", e);
}
catch (Exception e)
{
log.Error("InitDocument", e);
}
}
}
}

View File

@ -30,9 +30,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
@ -40,13 +39,12 @@ using ASC.Common.Logging;
using ASC.Core;
using ASC.Core.Common.EF;
using ASC.Core.Common.EF.Context;
using ASC.Core.Common.EF.Model;
using ASC.ElasticSearch.Core;
using ASC.ElasticSearch.Service;
using Autofac;
using Elasticsearch.Net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@ -57,71 +55,76 @@ namespace ASC.ElasticSearch
public class BaseIndexerHelper
{
public ConcurrentDictionary<string, bool> IsExist { get; set; }
private readonly ICacheNotify<SearchItem> Notify;
private readonly ICacheNotify<ClearIndexAction> Notify;
public BaseIndexerHelper(ICacheNotify<SearchItem> cacheNotify)
public BaseIndexerHelper(ICacheNotify<ClearIndexAction> cacheNotify)
{
IsExist = new ConcurrentDictionary<string, bool>();
Notify = cacheNotify;
Notify.Subscribe((a) =>
{
{
IsExist.AddOrUpdate(a.Id, false, (q, w) => false);
}, CacheNotifyAction.Any);
}
public void Clear<T>(T t) where T : Wrapper
public void Clear<T>(T t) where T : class, ISearchItem
{
Notify.Publish(new SearchItem() { Id = t.IndexName }, CacheNotifyAction.Any);
Notify.Publish(new ClearIndexAction() { Id = t.IndexName }, CacheNotifyAction.Any);
}
}
public class BaseIndexer<T> : IIndexer where T : Wrapper
public class BaseIndexer<T> where T : class, ISearchItem
{
private static readonly object Locker = new object();
protected internal T Wrapper { get { return ServiceProvider.GetService<T>(); } }
public string IndexName { get { return Wrapper.IndexName; } }
private const int QueryLimit = 1000;
public bool IsExist { get; set; }
public Client Client { get; }
public ILog Log { get; }
public TenantManager TenantManager { get; }
public SearchSettingsHelper SearchSettingsHelper { get; }
public BaseIndexerHelper BaseIndexerHelper { get; }
public BaseIndexerHelper BaseIndexerHelper { get; }
public Settings Settings { get; }
public IServiceProvider ServiceProvider { get; }
public WebstudioDbContext WebstudioDbContext { get; }
public WebstudioDbContext WebstudioDbContext { get; }
public BaseIndexer(
Client client,
IOptionsMonitor<ILog> log,
DbContextManager<WebstudioDbContext> dbContextManager,
TenantManager tenantManager,
SearchSettingsHelper searchSettingsHelper,
BaseIndexerHelper baseIndexerHelper,
BaseIndexerHelper baseIndexerHelper,
Settings settings,
IServiceProvider serviceProvider)
{
Client = client;
Log = log.CurrentValue;
TenantManager = tenantManager;
SearchSettingsHelper = searchSettingsHelper;
BaseIndexerHelper = baseIndexerHelper;
BaseIndexerHelper = baseIndexerHelper;
Settings = settings;
ServiceProvider = serviceProvider;
WebstudioDbContext = dbContextManager.Value;
}
internal void Index(T data, bool immediately = true)
{
BeforeIndex(data);
{
CreateIfNotExist(data);
Client.Instance.Index(data, idx => GetMeta(idx, data, immediately));
}
internal void Index(List<T> data, bool immediately = true)
{
if (!data.Any()) return;
CreateIfNotExist(data[0]);
if (typeof(T).IsSubclassOf(typeof(WrapperWithDoc)))
if (data[0] is ISearchItemDocument)
{
var currentLength = 0L;
var portion = new List<T>();
@ -132,17 +135,14 @@ namespace ASC.ElasticSearch
var t = data[i];
var runBulk = i == data.Count - 1;
BeforeIndex(t);
if (!(t is WrapperWithDoc wwd) || wwd.Document == null || string.IsNullOrEmpty(wwd.Document.Data))
if (!(t is ISearchItemDocument wwd) || wwd.Document == null || string.IsNullOrEmpty(wwd.Document.Data))
{
portion.Add(t);
}
else
{
var dLength = wwd.Document.Data.Length;
if (dLength >= Settings.Default.MemoryLimit)
if (dLength >= Settings.MemoryLimit)
{
try
{
@ -159,7 +159,7 @@ namespace ASC.ElasticSearch
continue;
}
if (currentLength + dLength < Settings.Default.MemoryLimit)
if (currentLength + dLength < Settings.MemoryLimit)
{
portion.Add(t);
currentLength += dLength;
@ -177,7 +177,7 @@ namespace ASC.ElasticSearch
Client.Instance.Bulk(r => r.IndexMany(portion1, GetMeta));
for (var j = portionStart; j < i; j++)
{
if (data[j] is WrapperWithDoc doc && doc.Document != null)
if (data[j] is ISearchItemDocument doc && doc.Document != null)
{
doc.Document.Data = null;
doc.Document = null;
@ -193,23 +193,18 @@ namespace ASC.ElasticSearch
}
else
{
foreach (var item in data)
{
BeforeIndex(item);
}
Client.Instance.Bulk(r => r.IndexMany(data, GetMeta));
}
}
internal void Update(T data, bool immediately = true, params Expression<Func<T, object>>[] fields)
{
{
CreateIfNotExist(data);
Client.Instance.Update(DocumentPath<T>.Id(data), r => GetMetaForUpdate(r, data, immediately, fields));
}
internal void Update(T data, UpdateAction action, Expression<Func<T, IList>> fields, bool immediately = true)
{
{
CreateIfNotExist(data);
Client.Instance.Update(DocumentPath<T>.Id(data), r => GetMetaForUpdate(r, data, action, fields, immediately));
}
@ -221,7 +216,7 @@ namespace ASC.ElasticSearch
}
internal void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, int tenantId, UpdateAction action, Expression<Func<T, IList>> fields, bool immediately = true)
{
{
CreateIfNotExist(data);
Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, action, fields, immediately));
}
@ -238,26 +233,26 @@ namespace ASC.ElasticSearch
public void Flush()
{
Client.Instance.Flush(new FlushRequest(IndexName));
Client.Instance.Indices.Flush(new FlushRequest(IndexName));
}
public void Refresh()
{
Client.Instance.Refresh(new RefreshRequest(IndexName));
Client.Instance.Indices.Refresh(new RefreshRequest(IndexName));
}
internal bool CheckExist(T data)
{
try
{
var isExist = BaseIndexerHelper.IsExist.GetOrAdd(data.IndexName, (k) => Client.Instance.IndexExists(k).Exists);
var isExist = BaseIndexerHelper.IsExist.GetOrAdd(data.IndexName, (k) => Client.Instance.Indices.Exists(k).Exists);
if (isExist) return true;
lock (Locker)
{
if (isExist) return true;
isExist = Client.Instance.IndexExists(data.IndexName).Exists;
isExist = Client.Instance.Indices.Exists(data.IndexName).Exists;
_ = BaseIndexerHelper.IsExist.TryUpdate(data.IndexName, IsExist, false);
@ -271,68 +266,7 @@ namespace ASC.ElasticSearch
return false;
}
void IIndexer.Check()
{
var data = ServiceProvider.GetService<T>();
if (!CheckExist(data)) return;
var result = false;
var currentMappings = Client.Instance.GetMapping<T>(r => r.Index(data.IndexName));
var newMappings = GetMappings(data).Invoke(new CreateIndexDescriptor(data.IndexName));
var newMappingDict = new Dictionary<string, string>();
var props = newMappings.Mappings.SelectMany(r => r.Value.Properties).ToList();
foreach (var prop in props.Where(r => r.Key.Property != null && r.Key.Property.Name != "Document"))
{
var propKey = prop.Key.Property.Name.ToLowerCamelCase();
var key = newMappings.Index.Name + "." + propKey;
if (prop.Key.Property.CustomAttributes.Any())
{
newMappingDict.Add(key, props.Any(r => r.Key == propKey && r.Value is INestedProperty) ? FieldType.Nested.GetStringValue() : prop.Value.Type);
}
if (prop.Value is ObjectProperty obj)
{
foreach (var objProp in obj.Properties)
{
newMappingDict.Add(key + "." + objProp.Key.Property.Name.ToLowerCamelCase(), objProp.Value.Type);
}
}
}
foreach (var ind in currentMappings.Indices)
{
foreach (var prop in ind.Value.Mappings.SelectMany(r => r.Value.Properties).Where(r => r.Key.Name != "document"))
{
var key = ind.Key.Name + "." + prop.Key.Name.ToLowerCamelCase();
if (!newMappingDict.Contains(new KeyValuePair<string, string>(key, prop.Value.Type)))
{
result = true;
break;
}
var nested = prop.Value as NestedProperty ?? prop.Value as ObjectProperty;
if (nested != null)
{
if (nested.Properties.Any(nProp => !newMappingDict.Contains(new KeyValuePair<string, string>(key + "." + nProp.Key.Name.ToLowerCamelCase(), nProp.Value.Type))))
{
result = true;
}
}
}
}
if (result)
{
Clear();
}
}
async Task IIndexer.ReIndex()
public async Task ReIndex()
{
Clear();
//((IIndexer) this).IndexAll();
@ -350,9 +284,9 @@ namespace ASC.ElasticSearch
WebstudioDbContext.SaveChanges();
Log.DebugFormat("Delete {0}", Wrapper.IndexName);
Client.Instance.DeleteIndex(Wrapper.IndexName);
BaseIndexerHelper.Clear(Wrapper);
CreateIfNotExist(ServiceProvider.GetService<T>());
Client.Instance.Indices.Delete(Wrapper.IndexName);
BaseIndexerHelper.Clear(Wrapper);
CreateIfNotExist(Wrapper);
}
internal IReadOnlyCollection<T> Select(Expression<Func<Selector<T>, Selector<T>>> expression, bool onlyId = false)
@ -371,132 +305,57 @@ namespace ASC.ElasticSearch
var result = Client.Instance.Search(descriptor.GetDescriptor(this, onlyId));
total = result.Total;
return result.Documents;
}
private void BeforeIndex(T data)
{
CreateIfNotExist(data);
if (data is WrapperWithDoc wrapperWithDoc)
{
wrapperWithDoc.InitDocument(SearchSettingsHelper.CanSearchByContent<T>(data.TenantId), Log);
}
}
private void CreateIfNotExist(T data)
{
try
{
if (CheckExist(data)) return;
lock (Locker)
{
var columns = data.GetAnalyzers();
var nestedColumns = data.GetNested();
if (!columns.Any() && !nestedColumns.Any())
{
Client.Instance.CreateIndex(data.IndexName);
}
else
{
Client.Instance.CreateIndex(data.IndexName, GetMappings(data));
}
IsExist = true;
}
}
catch (Exception e)
{
Log.Error("CreateIfNotExist", e);
}
}
public Func<CreateIndexDescriptor, ICreateIndexRequest> GetMappings(T data)
{
var columns = data.GetAnalyzers();
var nestedColumns = data.GetNested();
Func<AnalyzersDescriptor, IPromise<IAnalyzers>> analyzers = b =>
{
foreach (var c in Enum.GetNames(typeof(Analyzer)))
{
var c1 = c;
b.Custom(c1 + "custom", ca => ca.Tokenizer(c1).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString()));
}
foreach (var c in columns)
{
if (c.Value.CharFilter == CharFilter.io) continue;
var charFilters = new List<string>();
foreach (var r in Enum.GetValues(typeof(CharFilter)))
{
if ((c.Value.CharFilter & (CharFilter)r) == (CharFilter)r) charFilters.Add(r.ToString());
}
var c1 = c;
b.Custom(c1.Key, ca => ca.Tokenizer(c1.Value.Analyzer.ToString()).Filters(c1.Value.Filter.ToString()).CharFilters(charFilters));
}
if (data is WrapperWithDoc)
{
b.Custom("document", ca => ca.Tokenizer(Analyzer.whitespace.ToString()).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString()));
}
return b;
};
Func<PropertiesDescriptor<T>, IPromise<IProperties>> nestedSelector = p =>
{
foreach (var c in nestedColumns)
{
var isNested = c.Key.IsGenericType;
Type prop;
MethodInfo nested;
Type typeDescriptor;
if (isNested)
{
prop = c.Key.GenericTypeArguments[0];
nested = p.GetType().GetMethod("Nested");
typeDescriptor = typeof(NestedPropertyDescriptor<,>);
}
else
{
prop = c.Key;
nested = p.GetType().GetMethod("Object");
typeDescriptor = typeof(ObjectTypeDescriptor<,>);
}
var desc = typeDescriptor.MakeGenericType(typeof(T), prop);
var methods = desc.GetMethods();
var name = methods.FirstOrDefault(r => r.Name == "Name" && r.GetParameters().FirstOrDefault(q => q.ParameterType == typeof(PropertyName)) != null);
var autoMap = methods.FirstOrDefault(r => r.Name == "AutoMap" && r.GetParameters().Length == 2);
var props = methods.FirstOrDefault(r => r.Name == "Properties");
if (name == null || autoMap == null || props == null) continue;
var param = Expression.Parameter(desc, "a");
var nameFunc = Expression.Call(param, name, Expression.Constant(new PropertyName(c.Value.ToLowerCamelCase()))); //a.Name(value(Nest.PropertyName))
var autoMapFunc = Expression.Call(param, autoMap, Expression.Constant(null, typeof(IPropertyVisitor)), Expression.Constant(0)); //a.AutoMap()
var inst = (Wrapper)Activator.CreateInstance(prop);
var instMethods = prop.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
var getProperties = instMethods.First(r => r.Name == "GetProperties").MakeGenericMethod(prop);
var propsFunc = Expression.Call(param, props, Expression.Constant(getProperties.Invoke(inst, null))); //a.AutoMap()
var nestedFunc = Expression.Lambda(Expression.Block(nameFunc, autoMapFunc, propsFunc), param).Compile();
var fooRef = nested.MakeGenericMethod(prop);
fooRef.Invoke(p, new object[] { nestedFunc });//p.Nested<Wrapper>(r=> r.Name(c.Value.ToLowerCamelCase()).AutoMap().Properties(getProperties()))
}
return p;
};
return c =>
c.Settings(r => r.Analysis(a => a.Analyzers(analyzers).CharFilters(d => d.HtmlStrip(CharFilter.html.ToString()).Mapping(CharFilter.io.ToString(), m => m.Mappings("ё => е", "Ё => Е")))))
.Mappings(r => r.Map<T>(m => m.AutoMap<T>().Properties(data.GetProperties<T>()).Properties(nestedSelector)));
}
}
public void CreateIfNotExist(T data)
{
try
{
if (CheckExist(data)) return;
lock (Locker)
{
IPromise<IAnalyzers> analyzers(AnalyzersDescriptor b)
{
foreach (var c in Enum.GetNames(typeof(Analyzer)))
{
var c1 = c;
b.Custom(c1 + "custom", ca => ca.Tokenizer(c1).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString()));
}
foreach (var c in Enum.GetNames(typeof(CharFilter)))
{
if (c == CharFilter.io.ToString()) continue;
var charFilters = new List<string>() { CharFilter.io.ToString(), c };
var c1 = c;
b.Custom(c1 + "custom", ca => ca.Tokenizer(Analyzer.whitespace.ToString()).Filters(Filter.lowercase.ToString()).CharFilters(charFilters));
}
if (data is ISearchItemDocument)
{
b.Custom("document", ca => ca.Tokenizer(Analyzer.whitespace.ToString()).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString()));
}
return b;
}
var createIndexResponse = Client.Instance.Indices.Create(data.IndexName,
c =>
c.Map<T>(m => m.AutoMap())
.Settings(r => r.Analysis(a =>
a.Analyzers(analyzers)
.CharFilters(d => d.HtmlStrip(CharFilter.html.ToString())
.Mapping(CharFilter.io.ToString(), m => m.Mappings("ё => е", "Ё => Е"))))));
IsExist = true;
}
}
catch (Exception e)
{
Log.Error("CreateIfNotExist", e);
}
}
private IIndexRequest<T> GetMeta(IndexDescriptor<T> request, T data, bool immediately = true)
{
@ -507,7 +366,7 @@ namespace ASC.ElasticSearch
result.Refresh(Elasticsearch.Net.Refresh.True);
}
if (data is WrapperWithDoc)
if (data is ISearchItemDocument)
{
result.Pipeline("attachments");
}
@ -518,7 +377,7 @@ namespace ASC.ElasticSearch
{
var result = desc.Index(IndexName).Id(data.Id);
if (data is WrapperWithDoc)
if (data is ISearchItemDocument)
{
result.Pipeline("attachments");
}
@ -707,7 +566,50 @@ namespace ASC.ElasticSearch
var selector = ServiceProvider.GetService<Selector<T>>();
var descriptor = func(selector).Where(r => r.TenantId, tenantId);
return descriptor.GetDescriptorForUpdate(this, GetScriptForUpdate(data, action, fields), immediately);
}
}
public IEnumerable<List<T>> IndexAll(Func<DateTime, (int, int, int)> getCount, Func<long, long, DateTime, List<T>> getData)
{
var lastIndexed = WebstudioDbContext.WebstudioIndex
.Where(r => r.IndexName == Wrapper.IndexName)
.Select(r => r.LastModified)
.FirstOrDefault();
var (count, max, min) = getCount(lastIndexed);
Log.Debug($"Index: {IndexName}, Count {count}, Max: {max}, Min: {min}");
if (count != 0)
{
var step = (max - min + 1) / count;
if (step == 0)
{
step = 1;
}
if (step < QueryLimit)
{
step = QueryLimit;
}
for (var i = min; i <= max; i += step)
{
yield return getData(i, step, lastIndexed);
//FactoryIndexer<T>.Index(data.Cast<T>().ToList());
}
}
WebstudioDbContext.AddOrUpdate(r => r.WebstudioIndex, new DbWebstudioIndex()
{
IndexName = Wrapper.IndexName,
LastModified = DateTime.UtcNow
});
WebstudioDbContext.SaveChanges();
Log.Debug($"index completed {Wrapper.IndexName}");
}
}
static class CamelCaseExtension
@ -734,7 +636,7 @@ namespace ASC.ElasticSearch
return services.AddKafkaService();
}
public static DIHelper AddBaseIndexerService<T>(this DIHelper services) where T : Wrapper
public static DIHelper AddBaseIndexerService<T>(this DIHelper services) where T : class, ISearchItem
{
services.TryAddScoped<BaseIndexer<T>>();

View File

@ -49,12 +49,14 @@ namespace ASC.ElasticSearch
public ILog Log { get; }
public CoreConfiguration CoreConfiguration { get; }
public Client(IOptionsMonitor<ILog> option, CoreConfiguration coreConfiguration)
public CoreConfiguration CoreConfiguration { get; }
public Settings Settings { get; }
public Client(IOptionsMonitor<ILog> option, CoreConfiguration coreConfiguration, Settings settings)
{
Log = option.Get("ASC.Indexer");
CoreConfiguration = coreConfiguration;
CoreConfiguration = coreConfiguration;
Settings = settings;
}
public ElasticClient Instance
@ -67,8 +69,7 @@ namespace ASC.ElasticSearch
{
if (client != null) return client;
var launchSettings = CoreConfiguration.GetSection<Settings>(Tenant.DEFAULT_TENANT) ??
Settings.Default;
var launchSettings = CoreConfiguration.GetSection<Settings>(Tenant.DEFAULT_TENANT) ?? Settings;
var uri = new Uri(string.Format("{0}://{1}:{2}", launchSettings.Scheme, launchSettings.Host, launchSettings.Port));
var settings = new ConnectionSettings(new SingleNodeConnectionPool(uri))
@ -107,7 +108,7 @@ namespace ASC.ElasticSearch
if (result.IsValid)
{
client.PutPipeline("attachments", p => p
client.Ingest.PutPipeline("attachments", p => p
.Processors(pp => pp
.Attachment<Attachment>(a => a.Field("document.data").TargetField("document.attachment"))
));
@ -132,7 +133,8 @@ namespace ASC.ElasticSearch
public static DIHelper AddClientService(this DIHelper services)
{
services.TryAddScoped<Client>();
return services
return services
.AddSettingsService()
.AddCoreConfigurationService();
}
}

View File

@ -52,89 +52,68 @@ using Nest;
namespace ASC.ElasticSearch
{
public class FactoryIndexerHelper
{
public ICache Cache { get; }
public ILog Logger { get; }
public FactoryIndexer FactoryIndexer { get; }
{
public DateTime LastIndexed { get; set; }
public string Indexing { get; set; }
public FactoryIndexerHelper(IOptionsMonitor<ILog> options, FactoryIndexer factoryIndexer)
{
Cache = AscCache.Memory;
Logger = options.Get("ASC.Indexer");
FactoryIndexer = factoryIndexer;
public FactoryIndexerHelper(ICacheNotify<IndexAction> cacheNotify)
{
cacheNotify.Subscribe((a) =>
{
if (a.LastIndexed != 0)
{
LastIndexed = new DateTime(a.LastIndexed);
}
Indexing = a.Indexing;
}, CacheNotifyAction.Any);
}
public bool Support<T>(T t) where T : Wrapper
{
if (!FactoryIndexer.CheckState()) return false;
var cacheTime = DateTime.UtcNow.AddMinutes(15);
var key = "elasticsearch " + t.IndexName;
try
{
var cacheValue = Cache.Get<string>(key);
if (!string.IsNullOrEmpty(cacheValue))
{
return Convert.ToBoolean(cacheValue);
}
//TODO:
//var service = new Service.Service();
//var result = service.Support(t.IndexName);
//Cache.Insert(key, result.ToString(CultureInfo.InvariantCulture).ToLower(), cacheTime);
return true;
}
catch (Exception e)
{
Cache.Insert(key, "false", cacheTime);
Logger.Error("FactoryIndexer CheckState", e);
return false;
}
}
}
public interface IFactoryIndexer
{
void IndexAll();
string IndexName { get; }
void ReIndex();
string SettingsTitle { get; }
}
public class FactoryIndexer<T> where T : Wrapper
public class FactoryIndexer<T> : IFactoryIndexer where T : class, ISearchItem
{
private static readonly TaskScheduler Scheduler = new LimitedConcurrencyLevelTaskScheduler(10);
public ILog Logger { get; }
public FactoryIndexerHelper FactoryIndexerHelper { get; }
public TenantManager TenantManager { get; }
public SearchSettingsHelper SearchSettingsHelper { get; }
public FactoryIndexer FactoryIndexerCommon { get; }
public BaseIndexer<T> Indexer { get; }
public Client Client { get; }
public IServiceProvider ServiceProvider { get; }
public string IndexName { get => Indexer.IndexName; }
public ICache Cache { get; }
public virtual string SettingsTitle { get => ""; }
public FactoryIndexer(
IOptionsMonitor<ILog> options,
FactoryIndexerHelper factoryIndexerSupport,
TenantManager tenantManager,
SearchSettingsHelper searchSettingsHelper,
FactoryIndexer factoryIndexer,
BaseIndexer<T> baseIndexer,
Client client,
IServiceProvider serviceProvider)
{
{
Cache = AscCache.Memory;
Logger = options.Get("ASC.Indexer");
FactoryIndexerHelper = factoryIndexerSupport;
TenantManager = tenantManager;
SearchSettingsHelper = searchSettingsHelper;
FactoryIndexerCommon = factoryIndexer;
Indexer = baseIndexer;
Client = client;
ServiceProvider = serviceProvider;
ServiceProvider = serviceProvider;
}
public bool TrySelect(Expression<Func<Selector<T>, Selector<T>>> expression, out IReadOnlyCollection<T> result)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t))
if (!Support(t) || !Indexer.CheckExist(t))
{
result = new List<T>();
return false;
@ -156,7 +135,7 @@ namespace ASC.ElasticSearch
public bool TrySelectIds(Expression<Func<Selector<T>, Selector<T>>> expression, out List<int> result)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t))
if (!Support(t) || !Indexer.CheckExist(t))
{
result = new List<int>();
return false;
@ -179,7 +158,7 @@ namespace ASC.ElasticSearch
public bool TrySelectIds(Expression<Func<Selector<T>, Selector<T>>> expression, out List<int> result, out long total)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t))
if (!Support(t) || !Indexer.CheckExist(t))
{
result = new List<int>();
total = 0;
@ -209,7 +188,7 @@ namespace ASC.ElasticSearch
public bool Index(T data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return false;
if (!Support(t)) return false;
try
{
@ -226,7 +205,7 @@ namespace ASC.ElasticSearch
public void Index(List<T> data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t) || !data.Any()) return;
if (!Support(t) || !data.Any()) return;
try
{
@ -258,7 +237,7 @@ namespace ASC.ElasticSearch
public void Update(T data, bool immediately = true, params Expression<Func<T, object>>[] fields)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
try
{
@ -273,7 +252,7 @@ namespace ASC.ElasticSearch
public void Update(T data, UpdateAction action, Expression<Func<T, IList>> field, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
try
{
@ -288,7 +267,7 @@ namespace ASC.ElasticSearch
public void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, bool immediately = true, params Expression<Func<T, object>>[] fields)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
try
{
@ -304,7 +283,7 @@ namespace ASC.ElasticSearch
public void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, UpdateAction action, Expression<Func<T, IList>> fields, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
try
{
@ -320,7 +299,7 @@ namespace ASC.ElasticSearch
public void Delete(T data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
try
{
@ -335,7 +314,7 @@ namespace ASC.ElasticSearch
public void Delete(Expression<Func<Selector<T>, Selector<T>>> expression, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
var tenant = TenantManager.GetCurrentTenant().TenantId;
@ -352,35 +331,35 @@ namespace ASC.ElasticSearch
public Task<bool> IndexAsync(T data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false);
if (!Support(t)) return Task.FromResult(false);
return Queue(() => Indexer.Index(data, immediately));
}
public Task<bool> IndexAsync(List<T> data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false);
if (!Support(t)) return Task.FromResult(false);
return Queue(() => Indexer.Index(data, immediately));
}
public Task<bool> UpdateAsync(T data, bool immediately = true, params Expression<Func<T, object>>[] fields)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false);
if (!Support(t)) return Task.FromResult(false);
return Queue(() => Indexer.Update(data, immediately, fields));
}
public Task<bool> DeleteAsync(T data, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false);
if (!Support(t)) return Task.FromResult(false);
return Queue(() => Indexer.Delete(data, immediately));
}
public Task<bool> DeleteAsync(Expression<Func<Selector<T>, Selector<T>>> expression, bool immediately = true)
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false);
if (!Support(t)) return Task.FromResult(false);
var tenant = TenantManager.GetCurrentTenant().TenantId;
return Queue(() => Indexer.Delete(expression, tenant, immediately));
}
@ -389,14 +368,14 @@ namespace ASC.ElasticSearch
public void Flush()
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
Indexer.Flush();
}
public void Refresh()
{
var t = ServiceProvider.GetService<T>();
if (!FactoryIndexerHelper.Support(t)) return;
if (!Support(t)) return;
Indexer.Refresh();
}
@ -423,24 +402,79 @@ namespace ASC.ElasticSearch
task.ConfigureAwait(false);
task.Start(Scheduler);
return task;
}
}
public virtual void IndexAll()
{
return;
}
public void ReIndex()
{
Indexer.ReIndex();
}
public bool Support(T t)
{
if (!FactoryIndexerCommon.CheckState()) return false;
var cacheTime = DateTime.UtcNow.AddMinutes(15);
var key = "elasticsearch " + t.IndexName;
try
{
var cacheValue = Cache.Get<string>(key);
if (!string.IsNullOrEmpty(cacheValue))
{
return Convert.ToBoolean(cacheValue);
}
//TODO:
//var service = new Service.Service();
//var result = service.Support(t.IndexName);
//Cache.Insert(key, result.ToString(CultureInfo.InvariantCulture).ToLower(), cacheTime);
return true;
}
catch (Exception e)
{
Cache.Insert(key, "false", cacheTime);
Logger.Error("FactoryIndexer CheckState", e);
return false;
}
}
}
public class FactoryIndexer
{
private static ICache cache = AscCache.Memory;
internal IContainer Builder { get; set; }
private static ICache cache = AscCache.Memory;
public FactoryIndexerHelper FactoryIndexerHelper { get; }
internal ILifetimeScope Builder { get; set; }
internal static bool Init { get; set; }
public ILog Log { get; }
public Client Client { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public FactoryIndexer(
IContainer container,
ILifetimeScope container,
FactoryIndexerHelper factoryIndexerHelper,
Client client,
IOptionsMonitor<ILog> options,
CoreBaseSettings coreBaseSettings) : this(null, factoryIndexerHelper, client, options, coreBaseSettings)
{
Builder = container;
}
public FactoryIndexer(
IContainer container,
FactoryIndexerHelper factoryIndexerHelper,
Client client,
IOptionsMonitor<ILog> options,
CoreBaseSettings coreBaseSettings)
{
FactoryIndexerHelper = factoryIndexerHelper;
Client = client;
CoreBaseSettings = coreBaseSettings;
@ -507,23 +541,23 @@ namespace ASC.ElasticSearch
public object GetState(TenantUtil tenantUtil)
{
var indices = CoreBaseSettings.Standalone ?
Client.Instance.CatIndices(new CatIndicesRequest { SortByColumns = new[] { "index" } }).Records.Select(r => new
Client.Instance.Cat.Indices(new CatIndicesRequest { SortByColumns = new[] { "index" } }).Records.Select(r => new
{
r.Index,
r.DocsCount,
r.StoreSize
}) :
}) :
null;
State state = null;
if (CoreBaseSettings.Standalone)
{
//TODO
//using (var service = new ServiceClient())
//{
// state = service.GetState();
//}
{
state = new State
{
Indexing = FactoryIndexerHelper.Indexing,
LastIndexed = FactoryIndexerHelper.LastIndexed != DateTime.MinValue ? FactoryIndexerHelper.LastIndexed : default(DateTime?)
};
if (state.LastIndexed.HasValue)
{
@ -544,9 +578,9 @@ namespace ASC.ElasticSearch
if (!CoreBaseSettings.Standalone) return;
var generic = typeof(BaseIndexer<>);
var indexers = Builder.Resolve<IEnumerable<Wrapper>>()
var indexers = Builder.Resolve<IEnumerable<IFactoryIndexer>>()
.Where(r => string.IsNullOrEmpty(name) || r.IndexName == name)
.Select(r => (IIndexer)Activator.CreateInstance(generic.MakeGenericType(r.GetType()), r));
.Select(r => (IFactoryIndexer)Activator.CreateInstance(generic.MakeGenericType(r.GetType()), r));
foreach (var indexer in indexers)
{
@ -559,25 +593,23 @@ namespace ASC.ElasticSearch
{
public static DIHelper AddFactoryIndexerService(this DIHelper services)
{
services.TryAddSingleton<FactoryIndexerHelper>();
services.TryAddScoped<FactoryIndexer>();
return services
.AddClientService()
.AddCoreBaseSettingsService();
}
public static DIHelper AddFactoryIndexerHelperService(this DIHelper services)
{
services.TryAddScoped<FactoryIndexerHelper>();
return services
.AddFactoryIndexerService();
}
public static DIHelper AddFactoryIndexerService<T>(this DIHelper services) where T : Wrapper
{
services.TryAddScoped<FactoryIndexer<T>>();
public static DIHelper AddFactoryIndexerService<T>(this DIHelper services, bool addBase = true) where T : class, ISearchItem
{
if (addBase)
{
services.TryAddScoped<FactoryIndexer<T>>();
}
services.TryAddScoped<Selector<T>>();
return services
.AddFactoryIndexerHelperService()
.AddTenantManagerService()
.AddFactoryIndexerService()
.AddClientService()

View File

@ -23,16 +23,15 @@
*
*/
using System.Threading.Tasks;
namespace ASC.ElasticSearch
{
internal interface IIndexer
public interface IIndexer
{
string IndexName { get; }
void Check();
void IndexAll();
Task ReIndex();
}

View File

@ -0,0 +1,196 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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.Threading;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.Logging;
using ASC.ElasticSearch.Service;
using Autofac;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
namespace ASC.ElasticSearch
{
public class ServiceLauncher : IHostedService
{
private ILog Log { get; }
private ICacheNotify<AscCacheItem> Notify { get; }
public ICacheNotify<IndexAction> IndexNotify { get; }
public IServiceProvider ServiceProvider { get; }
public IContainer Container { get; }
private bool IsStarted { get; set; }
private CancellationTokenSource CancellationTokenSource { get; set; }
private Timer Timer { get; set; }
private TimeSpan Period { get; set; }
public ServiceLauncher(
IOptionsMonitor<ILog> options,
ICacheNotify<AscCacheItem> notify,
ICacheNotify<IndexAction> indexNotify,
IServiceProvider serviceProvider,
IContainer container,
Settings settings)
{
Log = options.Get("ASC.Indexer");
Notify = notify;
IndexNotify = indexNotify;
ServiceProvider = serviceProvider;
Container = container;
CancellationTokenSource = new CancellationTokenSource();
Period = TimeSpan.FromMinutes(settings.Period.Value);
}
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
Notify.Subscribe(async (item) =>
{
while (IsStarted)
{
await Task.Delay(10000);
}
IndexAll(true);
}, CacheNotifyAction.Any);
}
catch (Exception e)
{
Log.Error("Subscribe on start", e);
}
var task = new Task(async () =>
{
using var scope = ServiceProvider.CreateScope();
var factoryIndexer = scope.ServiceProvider.GetService<FactoryIndexer>();
var service = scope.ServiceProvider.GetService<Service.Service>();
while (!factoryIndexer.CheckState(false))
{
if (CancellationTokenSource.IsCancellationRequested)
{
return;
}
await Task.Delay(10000);
}
service.Subscribe();
Timer = new Timer(_ => IndexAll(), null, TimeSpan.Zero, TimeSpan.Zero);
}, CancellationTokenSource.Token, TaskCreationOptions.LongRunning);
task.Start();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
IsStarted = false;
if (Timer != null)
{
Timer.Dispose();
}
CancellationTokenSource.Cancel();
return Task.CompletedTask;
}
private void IndexAll(bool reindex = false)
{
Timer.Change(-1, -1);
IsStarted = true;
using var scope = Container.BeginLifetimeScope();
var wrappers = scope.Resolve<IEnumerable<IFactoryIndexer>>();
foreach (var w in wrappers)
{
IndexProduct(w, reindex);
}
Timer.Change(Period, Period);
IndexNotify.Publish(new IndexAction() { Indexing = "", LastIndexed = DateTime.Now.Ticks }, CacheNotifyAction.Any);
IsStarted = false;
}
public void IndexProduct(IFactoryIndexer product, bool reindex)
{
if (reindex)
{
try
{
if (!IsStarted) return;
Log.DebugFormat("Product reindex {0}", product.IndexName);
product.ReIndex();
}
catch (Exception e)
{
Log.Error(e);
Log.ErrorFormat("Product reindex {0}", product.IndexName);
}
}
try
{
if (!IsStarted) return;
Log.DebugFormat("Product {0}", product.IndexName);
IndexNotify.Publish(new IndexAction() { Indexing = product.IndexName, LastIndexed = 0 }, CacheNotifyAction.Any);
product.IndexAll();
}
catch (Exception e)
{
Log.Error(e);
Log.ErrorFormat("Product {0}", product.IndexName);
}
}
}
public static class ServiceLauncherExtension
{
public static DIHelper AddServiceLauncher(this DIHelper services)
{
services.TryAddSingleton<ServiceLauncher>();
services.TryAddSingleton<Service.Service>();
return services
.AddSettingsService()
.AddFactoryIndexerService();
}
}
}

View File

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASC.Common.Caching;
using ASC.Core;
using ASC.Core.Common.Settings;
using ASC.ElasticSearch.Core;
@ -38,26 +39,36 @@ using Autofac;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.ElasticSearch.Service
{
{
public class Service
{
public FactoryIndexer FactoryIndexer { get; }
public IContainer Container { get; }
public IServiceProvider ServiceProvider { get; }
public ICacheNotify<ReIndexAction> CacheNotify { get; }
public Service(FactoryIndexer factoryIndexer, IServiceProvider serviceProvider)
public Service(IContainer container, IServiceProvider serviceProvider, ICacheNotify<ReIndexAction> cacheNotify)
{
FactoryIndexer = factoryIndexer;
Container = container;
ServiceProvider = serviceProvider;
CacheNotify = cacheNotify;
}
public void Subscribe()
{
CacheNotify.Subscribe((a) =>
{
ReIndex(a.Names.ToList(), a.Tenant);
}, CacheNotifyAction.Any);
}
public bool Support(string table)
{
return FactoryIndexer.Builder.Resolve<IEnumerable<Wrapper>>().Any(r => r.IndexName == table);
return Container.Resolve<IEnumerable<IFactoryIndexer>>().Any(r => r.IndexName == table);
}
public void ReIndex(List<string> toReIndex, int tenant)
{
var allItems = FactoryIndexer.Builder.Resolve<IEnumerable<Wrapper>>().ToList();
var allItems = Container.Resolve<IEnumerable<IFactoryIndexer>>().ToList();
var tasks = new List<Task>(toReIndex.Count);
foreach (var item in toReIndex)
@ -69,6 +80,8 @@ namespace ASC.ElasticSearch.Service
var instance = (IIndexer)Activator.CreateInstance(generic.MakeGenericType(index.GetType()), index);
tasks.Add(instance.ReIndex());
}
if (!tasks.Any()) return;
Task.WhenAll(tasks).ContinueWith(r =>
{

View File

@ -24,54 +24,47 @@
*/
using System.Configuration;
using ASC.ElasticSearch.Config;
using ASC.Common;
using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
namespace ASC.ElasticSearch.Service
{
public class Settings
{
private static readonly Settings DefaultSettings;
static Settings()
public Settings()
{
}
public Settings(IConfiguration configuration)
{
DefaultSettings = new Settings
{
Scheme = "http",
Host = "localhost",
Port = 9200,
Period = 1,
MemoryLimit = 10 * 1024 * 1024L
};
var cfg = ConfigurationManager.GetSection("elastic") as ElasticSection;
if (cfg == null) return;
DefaultSettings = new Settings
{
Scheme = cfg.Scheme,
Host = cfg.Host,
Port = cfg.Port,
Period = cfg.Period,
MemoryLimit = cfg.MemoryLimit
};
var cfg = configuration.GetSetting<Settings>("elastic");
Scheme = cfg.Scheme ?? "http";
Host = cfg.Host ?? "localhost";
Port = cfg.Port ?? 9200;
Period = cfg.Period ?? 1;
MemoryLimit = cfg.MemoryLimit ?? 10 * 1024 * 1024L;
}
public string Host { get; set; }
public int Port { get; set; }
public int? Port { get; set; }
public string Scheme { get; set; }
public int Period { get; set; }
public int? Period { get; set; }
public long MemoryLimit { get; set; }
public static Settings Default
{
get { return DefaultSettings; }
public long? MemoryLimit { get; set; }
}
public static class SettingsExtention
{
public static DIHelper AddSettingsService(this DIHelper services)
{
services.TryAddSingleton<Settings>();
return services;
}
}
}

View File

@ -2,6 +2,16 @@
package ASC.ElasticSearch;
message SearchItem {
message ClearIndexAction {
string Id = 1;
}
message ReIndexAction {
int32 Tenant = 1;
repeated string Names = 2;
}
message IndexAction {
string Indexing = 1;
int64 LastIndexed = 2;
}

View File

@ -35,6 +35,7 @@ using ASC.Files.Resources;
using ASC.Web.Core;
using ASC.Web.Core.PublicResources;
using ASC.Web.Files.Classes;
using ASC.Web.Files.Core.Search;
namespace ASC.Web.Files.Configuration
{
@ -46,6 +47,8 @@ namespace ASC.Web.Files.Configuration
public CoreBaseSettings CoreBaseSettings { get; }
public AuthContext AuthContext { get; }
public UserManager UserManager { get; }
public IServiceProvider ServiceProvider { get; }
//public SubscriptionManager SubscriptionManager { get; }
public ProductEntryPoint()
@ -97,6 +100,7 @@ namespace ASC.Web.Files.Configuration
UserOpportunities = userOpportunities,
CanNotBeDisabled = true,
};
//SearchHandlerManager.Registry(new SearchHandler());
}
@ -177,7 +181,8 @@ namespace ASC.Web.Files.Configuration
.AddAuthContextService()
.AddUserManagerService()
.AddGlobalService()
.AddFilesSubscriptionManagerService();
.AddFilesSubscriptionManagerService()
.AddFactoryIndexerFileService();
}
}
}

View File

@ -53,8 +53,10 @@ namespace ASC.Files.Core.Data
public FilesDbContext FilesDbContext { get; }
protected internal int TenantID { get; }
private int tenantID;
protected internal int TenantID { get => tenantID != 0 ? tenantID : (tenantID = TenantManager.GetCurrentTenant().TenantId); }
public UserManager UserManager { get; }
public TenantManager TenantManager { get; }
public TenantUtil TenantUtil { get; }
public SetupInfo SetupInfo { get; }
public TenantExtra TenantExtra { get; }
@ -81,8 +83,8 @@ namespace ASC.Files.Core.Data
{
cache = AscCache.Memory;
FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId);
TenantID = tenantManager.GetCurrentTenant().TenantId;
UserManager = userManager;
TenantManager = tenantManager;
TenantUtil = tenantUtil;
SetupInfo = setupInfo;
TenantExtra = tenantExtra;
@ -100,7 +102,7 @@ namespace ASC.Files.Core.Data
return set.Where(r => r.TenantId == TenantID);
}
protected IQueryable<DbFile> GetFileQuery(Expression<Func<DbFile, bool>> where)
protected internal IQueryable<DbFile> GetFileQuery(Expression<Func<DbFile, bool>> where)
{
return Query(FilesDbContext.Files)
.Where(where);

View File

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using ASC.Common;
using ASC.Core;
@ -55,8 +56,10 @@ namespace ASC.Files.Core.Data
{
internal class FileDao : AbstractDao, IFileDao<int>
{
public const long MaxContentLength = 2 * 1024 * 1024 * 1024L;
private static readonly object syncRoot = new object();
public FactoryIndexer<FilesWrapper> FactoryIndexer { get; }
public FactoryIndexer<DbFile> FactoryIndexer { get; }
public GlobalStore GlobalStore { get; }
public GlobalSpace GlobalSpace { get; }
public GlobalFolder GlobalFolder { get; }
@ -66,7 +69,7 @@ namespace ASC.Files.Core.Data
public CrossDao CrossDao { get; }
public FileDao(
FactoryIndexer<FilesWrapper> factoryIndexer,
FactoryIndexer<DbFile> factoryIndexer,
UserManager userManager,
DbContextManager<FilesDbContext> dbContextManager,
TenantManager tenantManager,
@ -252,7 +255,7 @@ namespace ASC.Files.Core.Data
{
var func = GetFuncForSearch(parentId, orderBy, filterType, subjectGroup, subjectID, searchText, searchInContent, withSubfolders);
Expression<Func<Selector<FilesWrapper>, Selector<FilesWrapper>>> expression = s => func(s);
Expression<Func<Selector<DbFile>, Selector<DbFile>>> expression = s => func(s);
if (FactoryIndexer.TrySelectIds(expression, out var searchIds))
{
@ -368,6 +371,8 @@ namespace ASC.Files.Core.Data
var isNew = false;
List<int> parentFoldersIds;
DbFile toInsert = null;
lock (syncRoot)
{
using var tx = FilesDbContext.Database.BeginTransaction();
@ -400,7 +405,7 @@ namespace ASC.Files.Core.Data
FilesDbContext.SaveChanges();
}
var toInsert = new DbFile
toInsert = new DbFile
{
Id = file.ID,
Version = file.Version,
@ -428,14 +433,15 @@ namespace ASC.Files.Core.Data
file.PureTitle = file.Title;
parentFoldersIds =
var parentFolders =
FilesDbContext.Tree
.Where(r => r.FolderId == file.FolderID)
.OrderByDescending(r => r.Level)
.Select(r => r.ParentId)
.ToList();
if (parentFoldersIds.Count > 0)
parentFoldersIds = parentFolders.Select(r => r.ParentId).ToList();
if (parentFoldersIds.Any())
{
var folderToUpdate = FilesDbContext.Folders
.Where(r => parentFoldersIds.Any(a => a == r.Id));
@ -449,6 +455,8 @@ namespace ASC.Files.Core.Data
FilesDbContext.SaveChanges();
}
toInsert.Folders = parentFolders;
if (isNew)
{
RecalculateFilesCount(file.FolderID);
@ -476,7 +484,7 @@ namespace ASC.Files.Core.Data
}
}
FactoryIndexer.IndexAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file, parentFoldersIds));
FactoryIndexer.IndexAsync(InitDocument(toInsert));
return GetFile(file.ID);
}
@ -502,6 +510,8 @@ namespace ASC.Files.Core.Data
}
}
DbFile toUpdate = null;
List<int> parentFoldersIds;
lock (syncRoot)
{
@ -516,7 +526,7 @@ namespace ASC.Files.Core.Data
if (file.CreateBy == default) file.CreateBy = AuthContext.CurrentAccount.ID;
if (file.CreateOn == default) file.CreateOn = TenantUtil.DateTimeNow();
var toUpdate = FilesDbContext.Files
toUpdate = FilesDbContext.Files
.Where(r => r.Id == file.ID && r.Version == file.Version)
.FirstOrDefault();
@ -541,13 +551,14 @@ namespace ASC.Files.Core.Data
file.PureTitle = file.Title;
parentFoldersIds = FilesDbContext.Tree
var parentFolders = FilesDbContext.Tree
.Where(r => r.FolderId == file.FolderID)
.OrderByDescending(r => r.Level)
.Select(r => r.ParentId)
.ToList();
if (parentFoldersIds.Count > 0)
parentFoldersIds = parentFolders.Select(r => r.ParentId).ToList();
if (parentFoldersIds.Any())
{
var folderToUpdate = FilesDbContext.Folders
.Where(r => parentFoldersIds.Any(a => a == r.Id));
@ -560,6 +571,8 @@ namespace ASC.Files.Core.Data
FilesDbContext.SaveChanges();
}
toUpdate.Folders = parentFolders;
}
if (fileStream != null)
@ -579,7 +592,7 @@ namespace ASC.Files.Core.Data
}
}
FactoryIndexer.IndexAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file, parentFoldersIds));
FactoryIndexer.IndexAsync(InitDocument(toUpdate));
return GetFile(file.ID);
}
@ -639,6 +652,11 @@ namespace ASC.Files.Core.Data
var toDeleteFiles = Query(FilesDbContext.Files).Where(r => r.Id == fileId);
FilesDbContext.RemoveRange(toDeleteFiles);
foreach (var d in toDeleteFiles)
{
FactoryIndexer.DeleteAsync(d);
}
var toDeleteLinks = Query(FilesDbContext.TagLink).Where(r => r.EntryId == fileId.ToString()).Where(r => r.EntryType == FileEntryType.File);
FilesDbContext.RemoveRange(toDeleteFiles);
@ -661,9 +679,11 @@ namespace ASC.Files.Core.Data
if (deleteFolder)
DeleteFolder(fileId);
var wrapper = ServiceProvider.GetService<FilesWrapper>();
wrapper.Id = fileId;
FactoryIndexer.DeleteAsync(wrapper);
var toDeleteFile = toDeleteFiles.FirstOrDefault(r => r.CurrentVersion);
if (toDeleteFile != null)
{
FactoryIndexer.DeleteAsync(toDeleteFile);
}
}
public bool IsExist(string title, object folderId)
@ -699,6 +719,8 @@ namespace ASC.Files.Core.Data
{
if (fileId == default) return default;
List<DbFile> toUpdate;
using (var tx = FilesDbContext.Database.BeginTransaction())
{
var fromFolders = Query(FilesDbContext.Files)
@ -707,7 +729,7 @@ namespace ASC.Files.Core.Data
.Distinct()
.ToList();
var toUpdate = Query(FilesDbContext.Files)
toUpdate = Query(FilesDbContext.Files)
.Where(r => r.Id == fileId)
.ToList();
@ -729,20 +751,19 @@ namespace ASC.Files.Core.Data
RecalculateFilesCount(toFolderId);
}
var parentFoldersIds =
var parentFolders =
FilesDbContext.Tree
.Where(r => r.FolderId == toFolderId)
.OrderByDescending(r => r.Level)
.Select(r => r.ParentId)
.ToList();
var wrapper = ServiceProvider.GetService<FilesWrapper>();
wrapper.Id = fileId;
wrapper.Folders = parentFoldersIds.Select(r => new FilesFoldersWrapper() { FolderId = r.ToString() }).ToList();
var toUpdateFile = toUpdate.FirstOrDefault(r => r.CurrentVersion);
FactoryIndexer.Update(wrapper,
UpdateAction.Replace,
w => w.Folders);
if (toUpdateFile != null)
{
toUpdateFile.Folders = parentFolders;
FactoryIndexer.Update(toUpdateFile, UpdateAction.Replace, w => w.Folders);
}
return fileId;
}
@ -812,7 +833,6 @@ namespace ASC.Files.Core.Data
public int FileRename(File<int> file, string newTitle)
{
var fileIdString = file.ID.ToString();
newTitle = Global.ReplaceInvalidCharsAndTruncate(newTitle);
var toUpdate = Query(FilesDbContext.Files)
.Where(r => r.Id == file.ID)
@ -825,6 +845,8 @@ namespace ASC.Files.Core.Data
FilesDbContext.SaveChanges();
FactoryIndexer.UpdateAsync(toUpdate, true, r => r.Title, r => r.ModifiedBy, r => r.ModifiedOn);
return file.ID;
}
@ -1175,7 +1197,7 @@ namespace ASC.Files.Core.Data
#endregion
private Func<Selector<FilesWrapper>, Selector<FilesWrapper>> GetFuncForSearch(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false)
private Func<Selector<DbFile>, Selector<DbFile>> GetFuncForSearch(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false)
{
return s =>
{
@ -1187,11 +1209,11 @@ namespace ASC.Files.Core.Data
{
if (withSubfolders)
{
result.In(a => a.Folders.Select(r => r.FolderId), new[] { parentId.ToString() });
result.In(a => a.Folders.Select(r => r.ParentId), new[] { parentId });
}
else
{
result.InAll(a => a.Folders.Select(r => r.FolderId), new[] { parentId.ToString() });
result.InAll(a => a.Folders.Select(r => r.ParentId), new[] { parentId });
}
}
@ -1209,7 +1231,7 @@ namespace ASC.Files.Core.Data
// result.Sort(r => r.Title, orderBy.IsAsc);
// break;
case SortedByType.DateAndTime:
result.Sort(r => r.LastModifiedOn, orderBy.IsAsc);
result.Sort(r => r.ModifiedOn, orderBy.IsAsc);
break;
case SortedByType.DateAndTimeCreation:
result.Sort(r => r.CreateOn, orderBy.IsAsc);
@ -1314,6 +1336,36 @@ namespace ASC.Files.Core.Data
file.Forcesave = r.file.Forcesave;
return file;
}
internal protected DbFile InitDocument(DbFile dbFile)
{
if (!FactoryIndexer.CanSearchByContent())
{
dbFile.Document = new Document
{
Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(""))
};
return dbFile;
}
var file = ServiceProvider.GetService<File<int>>();
file.ID = dbFile.Id;
file.Title = dbFile.Title;
file.Version = dbFile.Version;
file.ContentLength = dbFile.ContentLength;
if (!IsExistOnStorage(file) || file.ContentLength > MaxContentLength) return dbFile;
using var stream = GetFileStream(file);
if (stream == null) return dbFile;
dbFile.Document = new Document
{
Data = Convert.ToBase64String(stream.GetCorrectBuffer())
};
return dbFile;
}
}
public class DbFileQuery
@ -1344,7 +1396,7 @@ namespace ASC.Files.Core.Data
.AddAuthContextService()
.AddGlobalStoreService()
.AddGlobalSpaceService()
.AddFactoryIndexerService<FilesWrapper>()
.AddFactoryIndexerFileService()
.AddGlobalFolderService()
.AddChunkedUploadSessionHolderService()
.AddFolderDaoService();

View File

@ -41,7 +41,6 @@ using ASC.Files.Core.Thirdparty;
using ASC.Files.Resources;
using ASC.Files.Thirdparty.ProviderDao;
using ASC.Web.Files.Classes;
using ASC.Web.Files.Core.Search;
using ASC.Web.Studio.Core;
using ASC.Web.Studio.UserControls.Statistics;
using ASC.Web.Studio.Utility;
@ -59,14 +58,14 @@ namespace ASC.Files.Core.Data
private const string trash = "trash";
private const string projects = "projects";
public FactoryIndexer<FoldersWrapper> FactoryIndexer { get; }
public FactoryIndexer<DbFolder> FactoryIndexer { get; }
public GlobalSpace GlobalSpace { get; }
public IDaoFactory DaoFactory { get; }
public ProviderFolderDao ProviderFolderDao { get; }
public CrossDao CrossDao { get; }
public FolderDao(
FactoryIndexer<FoldersWrapper> factoryIndexer,
FactoryIndexer<DbFolder> factoryIndexer,
UserManager userManager,
DbContextManager<FilesDbContext> dbContextManager,
TenantManager tenantManager,
@ -318,6 +317,8 @@ namespace ASC.Files.Core.Data
toUpdate.ModifiedBy = folder.ModifiedBy;
FilesDbContext.SaveChanges();
FactoryIndexer.IndexAsync(toUpdate);
}
else
{
@ -337,6 +338,7 @@ namespace ASC.Files.Core.Data
newFolder = FilesDbContext.Folders.Add(newFolder).Entity;
FilesDbContext.SaveChanges();
FactoryIndexer.IndexAsync(newFolder);
folder.ID = newFolder.Id;
//itself link
@ -380,7 +382,7 @@ namespace ASC.Files.Core.Data
RecalculateFoldersCount(folder.ID);
}
FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder));
//FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder));
return folder.ID;
}
@ -413,6 +415,11 @@ namespace ASC.Files.Core.Data
var folderToDelete = Query(FilesDbContext.Folders).Where(r => subfolders.Any(a => r.Id == a));
FilesDbContext.Folders.RemoveRange(folderToDelete);
foreach (var f in folderToDelete)
{
FactoryIndexer.DeleteAsync(f);
}
var treeToDelete = FilesDbContext.Tree.Where(r => subfolders.Any(a => r.FolderId == a));
FilesDbContext.Tree.RemoveRange(treeToDelete);
@ -445,7 +452,7 @@ namespace ASC.Files.Core.Data
RecalculateFoldersCount(parent);
}
FactoryIndexer.DeleteAsync(new FoldersWrapper { Id = id });
//FactoryIndexer.DeleteAsync(new FoldersWrapper { Id = id });
}
public TTo MoveFolder<TTo>(int folderId, TTo toFolderId, CancellationToken? cancellationToken)
@ -574,7 +581,7 @@ namespace ASC.Files.Core.Data
copy = GetFolder(SaveFolder(copy));
FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, copy));
//FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, copy));
return copy;
}
@ -673,6 +680,8 @@ namespace ASC.Files.Core.Data
FilesDbContext.SaveChanges();
FactoryIndexer.IndexAsync(toUpdate);
return folder.ID;
}
@ -958,7 +967,7 @@ namespace ASC.Files.Core.Data
#endregion
protected IQueryable<DbFolder> GetFolderQuery(Expression<Func<DbFolder, bool>> where = null)
protected internal IQueryable<DbFolder> GetFolderQuery(Expression<Func<DbFolder, bool>> where = null)
{
var q = Query(FilesDbContext.Folders);
if (where != null)
@ -1154,7 +1163,7 @@ namespace ASC.Files.Core.Data
services.TryAddTransient<Folder<string>>();
return services
.AddFactoryIndexerService<FoldersWrapper>()
.AddFactoryIndexerService<DbFolder>()
.AddTenantManagerService()
.AddUserManagerService()
.AddFilesDbContextService()

View File

@ -1,14 +1,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq.Expressions;
using ASC.Core.Common.EF;
using ASC.ElasticSearch;
using Microsoft.EntityFrameworkCore;
using Nest;
using ColumnAttribute = System.ComponentModel.DataAnnotations.Schema.ColumnAttribute;
namespace ASC.Files.Core.EF
{
public static class Tables
{
public const string File = "file";
public const string Tree = "tree";
public const string Folder = "folder";
}
[ElasticsearchType(RelationName = Tables.File)]
[Table("files_file")]
public class DbFile : BaseEntity, IDbFile, IDbSearch
public class DbFile : BaseEntity, IDbFile, IDbSearch, ISearchItemDocument
{
public int Id { get; set; }
public int Version { get; set; }
@ -22,6 +37,7 @@ namespace ASC.Files.Core.EF
[Column("folder_id")]
public int FolderId { get; set; }
[Text(Analyzer = "whitespacecustom")]
public string Title { get; set; }
[Column("content_length")]
@ -50,12 +66,30 @@ namespace ASC.Files.Core.EF
[Column("converted_type")]
public string ConvertedType { get; set; }
public string Comment { get; set; }
public string Changes { get; set; }
public bool Encrypted { get; set; }
public ForcesaveType Forcesave { get; set; }
[Nested]
[NotMapped]
public List<DbFolderTree> Folders { get; set; }
[NotMapped]
public string IndexName
{
get => Tables.File;
}
[NotMapped]
public Document Document { get; set; }
public Expression<Func<ISearchItem, object[]>> SearchContentFields
{
get => (a) => new[] { Title, Comment, Changes, Document.Attachment.Content };
}
public override object[] GetKeys()
{
return new object[] { TenantId, Id, Version };

View File

@ -27,7 +27,7 @@ namespace ASC.Files.Core.EF
public override object[] GetKeys()
{
return new object[] { TenantId, EntryType, EntryId, Owner };
return new object[] { TenantId, EntryId, EntryType, Subject };
}
}
@ -36,7 +36,7 @@ namespace ASC.Files.Core.EF
public static ModelBuilder AddDbFilesSecurity(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<DbFilesSecurity>()
.HasKey(c => new { c.TenantId, c.EntryType, c.EntryId, c.Owner });
.HasKey(c => new { c.TenantId, c.EntryId, c.EntryType, c.Subject });
return modelBuilder;
}

View File

@ -1,16 +1,25 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq.Expressions;
using ASC.ElasticSearch;
using Nest;
using ColumnAttribute = System.ComponentModel.DataAnnotations.Schema.ColumnAttribute;
namespace ASC.Files.Core.EF
{
[ElasticsearchType(RelationName = Tables.Folder)]
[Table("files_folder")]
public class DbFolder : IDbFile, IDbSearch
public class DbFolder : IDbFile, IDbSearch, ISearchItem
{
public int Id { get; set; }
[Column("parent_id")]
public int ParentId { get; set; }
[Text(Analyzer = "whitespacecustom")]
public string Title { get; set; }
[Column("folder_type")]
@ -32,5 +41,19 @@ namespace ASC.Files.Core.EF
public int TenantId { get; set; }
public int FoldersCount { get; set; }
public int FilesCount { get; set; }
[NotMapped]
public string IndexName
{
get => Tables.Folder;
}
public Expression<Func<ISearchItem, object[]>> SearchContentFields
{
get
{
return (a) => new[] { Title };
}
}
}
}

View File

@ -4,9 +4,12 @@ using ASC.Core.Common.EF;
using Microsoft.EntityFrameworkCore;
using Nest;
namespace ASC.Files.Core.EF
{
[ElasticsearchType(RelationName = Tables.Tree)]
[Table("files_folder_tree")]
public class DbFolderTree : BaseEntity
{

View File

@ -1,5 +1,6 @@
using ASC.Common;
using ASC.Core.Common.EF;
using ASC.Core.Common.EF.Model;
using Microsoft.EntityFrameworkCore;
@ -18,6 +19,7 @@ namespace ASC.Files.Core.EF
public DbSet<DbFilesTag> Tag { get; set; }
public DbSet<DbFilesThirdpartyApp> ThirdpartyApp { get; set; }
public DbSet<DbEncryptedData> EncryptedData { get; set; }
public DbSet<DbTenant> Tenants { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@ -29,7 +31,8 @@ namespace ASC.Files.Core.EF
.AddDbFilesThirdpartyIdMapping()
.AddDbFilesTagLink()
.AddDbFilesThirdpartyApp()
.AddDbEncryptedData();
.AddDbEncryptedData()
.AddDbTenant();
}
}

View File

@ -48,6 +48,7 @@ using ASC.ElasticSearch;
using ASC.FederatedLogin.LoginProviders;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.EF;
using ASC.Files.Core.Security;
using ASC.Files.Resources;
using ASC.MessagingSystem;
@ -81,8 +82,8 @@ namespace ASC.Web.Files.Services.WCFService
public FilesSettingsHelper FilesSettingsHelper { get; }
public AuthContext AuthContext { get; }
public UserManager UserManager { get; }
public FactoryIndexer<FoldersWrapper> FoldersIndexer { get; }
public FactoryIndexer<FilesWrapper> FilesIndexer { get; }
public FactoryIndexer<DbFolder> FoldersIndexer { get; }
public FactoryIndexer<DbFile> FilesIndexer { get; }
public FileUtility FileUtility { get; }
public FilesLinkUtility FilesLinkUtility { get; }
public BaseCommonLinkUtility BaseCommonLinkUtility { get; }
@ -123,8 +124,8 @@ namespace ASC.Web.Files.Services.WCFService
FilesSettingsHelper filesSettingsHelper,
AuthContext authContext,
UserManager userManager,
FactoryIndexer<FoldersWrapper> foldersIndexer,
FactoryIndexer<FilesWrapper> filesIndexer,
FactoryIndexer<DbFolder> foldersIndexer,
FactoryIndexer<DbFile> filesIndexer,
FileUtility fileUtility,
FilesLinkUtility filesLinkUtility,
BaseCommonLinkUtility baseCommonLinkUtility,
@ -415,10 +416,10 @@ namespace ASC.Web.Files.Services.WCFService
FilesMessageService.Send(folder, GetHttpHeaders(), MessageAction.FolderRenamed, folder.Title);
if (!folder.ProviderEntry)
{
FoldersIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder));
}
//if (!folder.ProviderEntry)
//{
// FoldersIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder));
//}
}
var tag = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folder).FirstOrDefault();
@ -745,10 +746,10 @@ namespace ASC.Web.Files.Services.WCFService
{
FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileRenamed, file.Title);
if (!file.ProviderEntry)
{
FilesIndexer.UpdateAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file), true, r => r.Title);
}
//if (!file.ProviderEntry)
//{
// FilesIndexer.UpdateAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file), true, r => r.Title);
//}
}
if (file.RootFolderType == FolderType.USER
@ -2061,8 +2062,8 @@ namespace ASC.Web.Files.Services.WCFService
.AddGlobalFolderHelperService()
.AddAuthContextService()
.AddUserManagerService()
.AddFoldersWrapperService()
.AddFilesWrapperService()
.AddFactoryIndexerFolderService()
.AddFactoryIndexerFileService()
.AddFilesLinkUtilityService()
.AddBaseCommonLinkUtilityService()
.AddCoreBaseSettingsService()

View File

@ -0,0 +1,127 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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 ASC.Common;
using ASC.Common.Logging;
using ASC.Core;
using ASC.ElasticSearch;
using ASC.ElasticSearch.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.EF;
using ASC.Files.Resources;
using Microsoft.Extensions.Options;
namespace ASC.Web.Files.Core.Search
{
public class FactoryIndexerFile : FactoryIndexer<DbFile>
{
public IDaoFactory DaoFactory { get; }
public FactoryIndexerFile(
IOptionsMonitor<ILog> options,
TenantManager tenantManager,
SearchSettingsHelper searchSettingsHelper,
FactoryIndexer factoryIndexer,
BaseIndexer<DbFile> baseIndexer,
IServiceProvider serviceProvider,
IDaoFactory daoFactory)
: base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider)
{
DaoFactory = daoFactory;
}
public override void IndexAll()
{
var fileDao = DaoFactory.GetFileDao<int>() as FileDao;
(int, int, int) getCount(DateTime lastIndexed)
{
var q = fileDao.FilesDbContext.Files
.Where(r => r.ModifiedOn >= lastIndexed)
.Where(r => r.CurrentVersion)
.Join(fileDao.FilesDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { f, t })
.Where(r => r.t.Status == ASC.Core.Tenants.TenantStatus.Active);
var count = q.GroupBy(a => a.f.Id).Count();
var min = count > 0 ? q.Min(r => r.f.Id) : 0;
var max = count > 0 ? q.Max(r => r.f.Id) : 0;
return (count, max, min);
}
List<DbFile> getData(long i, long step, DateTime lastIndexed) =>
fileDao.FilesDbContext.Files
.Where(r => r.ModifiedOn >= lastIndexed)
.Where(r => r.CurrentVersion)
.Where(r => r.Id >= i && r.Id <= i + step)
.Join(fileDao.FilesDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { f, t })
.Where(r => r.t.Status == ASC.Core.Tenants.TenantStatus.Active)
.Select(r => r.f)
.ToList();
try
{
foreach (var data in Indexer.IndexAll(getCount, getData))
{
data.ForEach(r =>
{
TenantManager.SetCurrentTenant(r.TenantId);
fileDao.InitDocument(r);
TenantManager.CurrentTenant = null;
});
Index(data);
}
}
catch (Exception e)
{
Logger.Error(e);
throw;
}
}
public override string SettingsTitle
{
get { return FilesCommonResource.IndexTitle; }
}
}
public static class FactoryIndexerFileExtention
{
public static DIHelper AddFactoryIndexerFileService(this DIHelper services)
{
services.TryAddTransient<DbFile>();
services.TryAddScoped<FactoryIndexer<DbFile>, FactoryIndexerFile>();
return services
.AddFactoryIndexerService<DbFile>(false);
}
}
}

View File

@ -0,0 +1,116 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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 ASC.Common;
using ASC.Common.Logging;
using ASC.Core;
using ASC.ElasticSearch;
using ASC.ElasticSearch.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.EF;
using Microsoft.Extensions.Options;
namespace ASC.Web.Files.Core.Search
{
public class FactoryIndexerFolder : FactoryIndexer<DbFolder>
{
public IDaoFactory DaoFactory { get; }
public FactoryIndexerFolder(
IOptionsMonitor<ILog> options,
TenantManager tenantManager,
SearchSettingsHelper searchSettingsHelper,
FactoryIndexer factoryIndexer,
BaseIndexer<DbFolder> baseIndexer,
IServiceProvider serviceProvider,
IDaoFactory daoFactory)
: base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider)
{
DaoFactory = daoFactory;
}
public override void IndexAll()
{
var folderDao = DaoFactory.GetFolderDao<int>() as FolderDao;
(int, int, int) getCount(DateTime lastIndexed)
{
var q =
folderDao.FilesDbContext.Folders
.Where(r => r.ModifiedOn >= lastIndexed)
.Join(folderDao.FilesDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { f, t })
.Where(r => r.t.Status == ASC.Core.Tenants.TenantStatus.Active);
var count = q.GroupBy(a => a.f.Id).Count();
var min = count > 0 ? q.Min(r => r.f.Id) : 0;
var max = count > 0 ? q.Max(r => r.f.Id) : 0;
return (count, max, min);
}
List<DbFolder> getData(long i, long step, DateTime lastIndexed) =>
folderDao.FilesDbContext.Folders
.Where(r => r.ModifiedOn >= lastIndexed)
.Where(r => r.Id >= i && r.Id <= i + step)
.Join(folderDao.FilesDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { f, t })
.Where(r => r.t.Status == ASC.Core.Tenants.TenantStatus.Active)
.Select(r => r.f)
.ToList();
try
{
foreach (var data in Indexer.IndexAll(getCount, getData))
{
Index(data);
}
}
catch (Exception e)
{
Logger.Error(e);
throw;
}
}
}
public static class FactoryIndexerFolderExtention
{
public static DIHelper AddFactoryIndexerFolderService(this DIHelper services)
{
services.TryAddTransient<DbFolder>();
services.TryAddScoped<FactoryIndexer<DbFolder>, FactoryIndexerFolder>();
return services
.AddFactoryIndexerService<DbFolder>(false)
.AddDaoFactoryService();
}
}
}

View File

@ -1,174 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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.IO;
using System.Linq;
using ASC.Common;
using ASC.Core;
using ASC.ElasticSearch;
using ASC.ElasticSearch.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Resources;
using ASC.Web.Core.Files;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Web.Files.Core.Search
{
public sealed class FilesWrapper : WrapperWithDoc
{
[Column("title", 1)]
public string Title { get; set; }
[ColumnLastModified("modified_on")]
public override DateTime LastModifiedOn { get; set; }
[ColumnMeta("version", 2)]
public int Version { get; set; }
[ColumnCondition("current_version", 3, true)]
public bool Current { get; set; }
[ColumnMeta("encrypted", 4)]
public bool Encrypted { get; set; }
[ColumnMeta("content_length", 5)]
public long ContentLength { get; set; }
[ColumnMeta("create_by", 6)]
public Guid CreateBy { get; set; }
[ColumnMeta("create_on", 7)]
public DateTime CreateOn { get; set; }
[ColumnMeta("category", 8)]
public int Category { get; set; }
[Join(JoinTypeEnum.Sub, "folder_id:folder_id")]
public List<FilesFoldersWrapper> Folders { get; set; }
protected override string Table { get { return "files_file"; } }
public FilesWrapper()
{
}
public FilesWrapper(IServiceProvider serviceProvider, TenantManager tenantManager, FileUtility fileUtility, IDaoFactory daoFactory)
{
ServiceProvider = serviceProvider;
TenantManager = tenantManager;
FileUtility = fileUtility;
DaoFactory = daoFactory;
}
public static FilesWrapper GetFilesWrapper<T>(IServiceProvider serviceProvider, File<T> d, List<int> parentFolders = null)
{
var wrapper = serviceProvider.GetService<FilesWrapper>();
var tenantManager = serviceProvider.GetService<TenantManager>();
wrapper.Id = Convert.ToInt32(d.ID);
wrapper.Title = d.Title;
wrapper.Version = d.Version;
wrapper.Encrypted = d.Encrypted;
wrapper.ContentLength = d.ContentLength;
wrapper.LastModifiedOn = d.ModifiedOn;
wrapper.TenantId = tenantManager.GetCurrentTenant().TenantId;
if (parentFolders != null)
{
wrapper.Folders = parentFolders.Select(r => new FilesFoldersWrapper { FolderId = r.ToString() }).ToList();
}
return wrapper;
}
protected override Stream GetDocumentStream()
{
TenantManager.SetCurrentTenant(TenantId);
if (Encrypted) return null;
if (!FileUtility.CanIndex(Title)) return null;
var fileDao = DaoFactory.GetFileDao<int>();
var file = ServiceProvider.GetService<File<int>>();
file.ID = Id;
file.Title = Title;
file.Version = Version;
file.ContentLength = ContentLength;
if (!fileDao.IsExistOnStorage(file)) return null;
if (file.ContentLength > MaxContentLength) return null;
return fileDao.GetFileStream(file);
}
public override string SettingsTitle
{
get { return FilesCommonResource.IndexTitle; }
}
private IServiceProvider ServiceProvider { get; }
private TenantManager TenantManager { get; }
private FileUtility FileUtility { get; }
private IDaoFactory DaoFactory { get; }
}
public sealed class FilesFoldersWrapper : Wrapper
{
[Column("parent_id", 1)]
public string FolderId { get; set; }
[ColumnId("")]
public override int Id { get; set; }
[ColumnTenantId("")]
public override int TenantId { get; set; }
[ColumnLastModified("")]
public override DateTime LastModifiedOn { get; set; }
protected override string Table { get { return "files_folder_tree"; } }
}
public static class FilesWrapperExtention
{
public static DIHelper AddFilesWrapperService(this DIHelper services)
{
services.TryAddTransient<FilesWrapper>();
return services
.AddTenantManagerService()
.AddFileUtilityService()
.AddDaoFactoryService()
.AddFactoryIndexerService<FilesWrapper>();
}
}
}

View File

@ -1,69 +0,0 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* 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 ASC.Common;
using ASC.Core;
using ASC.ElasticSearch;
using ASC.Files.Core;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Web.Files.Core.Search
{
public sealed class FoldersWrapper : Wrapper
{
[Column("title", 1)]
public string Title { get; set; }
[ColumnLastModified("modified_on")]
public override DateTime LastModifiedOn { get; set; }
protected override string Table { get { return "files_folder"; } }
public static FoldersWrapper GetFolderWrapper<T>(IServiceProvider serviceProvider, Folder<T> d)
{
var tenantManager = serviceProvider.GetService<TenantManager>();
return new FoldersWrapper
{
Id = Convert.ToInt32(d.ID),
Title = d.Title,
TenantId = tenantManager.GetCurrentTenant().TenantId
};
}
}
public static class FoldersWrapperExtention
{
public static DIHelper AddFoldersWrapperService(this DIHelper services)
{
services.TryAddTransient<FoldersWrapper>();
return services
.AddFactoryIndexerService<FoldersWrapper>();
}
}
}

View File

@ -73,11 +73,13 @@ namespace ASC.Files.Thirdparty
internal class ProviderAccountDao : IProviderDao
{
protected int TenantID { get; private set; }
private int tenantID;
protected int TenantID { get => tenantID != 0 ? tenantID : (tenantID = TenantManager.GetCurrentTenant().TenantId); }
public FilesDbContext FilesDbContext { get; }
public ILog Logger { get; }
public IServiceProvider ServiceProvider { get; }
public TenantUtil TenantUtil { get; }
public TenantManager TenantManager { get; }
public InstanceCrypto InstanceCrypto { get; }
public SecurityContext SecurityContext { get; }
public ConsumerFactory ConsumerFactory { get; }
@ -92,11 +94,11 @@ namespace ASC.Files.Thirdparty
DbContextManager<FilesDbContext> dbContextManager,
IOptionsMonitor<ILog> options)
{
TenantID = tenantManager.GetCurrentTenant().TenantId;
FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId);
Logger = options.Get("ASC.Files");
ServiceProvider = serviceProvider;
TenantUtil = tenantUtil;
TenantManager = tenantManager;
InstanceCrypto = instanceCrypto;
SecurityContext = securityContext;
ConsumerFactory = consumerFactory;

View File

@ -49,7 +49,8 @@ namespace ASC.Files.Thirdparty.ProviderDao
{
private readonly List<IDaoSelector> Selectors;
private int TenantID { get; set; }
private int tenantID;
private int TenantID { get => tenantID != 0 ? tenantID : (tenantID = TenantManager.GetCurrentTenant().TenantId); }
public ProviderDaoBase(
IServiceProvider serviceProvider,
@ -59,10 +60,10 @@ namespace ASC.Files.Thirdparty.ProviderDao
CrossDao crossDao)
{
ServiceProvider = serviceProvider;
TenantManager = tenantManager;
SecurityDao = securityDao;
TagDao = tagDao;
CrossDao = crossDao;
TenantID = tenantManager.GetCurrentTenant().TenantId;
Selectors = new List<IDaoSelector>
{
@ -77,6 +78,7 @@ namespace ASC.Files.Thirdparty.ProviderDao
}
public IServiceProvider ServiceProvider { get; }
public TenantManager TenantManager { get; }
public SecurityDao<string> SecurityDao { get; }
public TagDao<string> TagDao { get; }
public CrossDao CrossDao { get; }

View File

@ -18,10 +18,10 @@ namespace ASC.Files.Model
public class DownloadModel : BaseBatchModel<object>
{
public IEnumerable<ItemKeyValuePair<string, string>> FileConvertIds { get; set; }
public IEnumerable<ItemKeyValuePair<object, string>> FileConvertIds { get; set; }
public DownloadModel() : base()
{
FileConvertIds = new List<ItemKeyValuePair<string, string>>();
FileConvertIds = new List<ItemKeyValuePair<object, string>>();
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<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="..\Server\ASC.Files.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.DependencyInjection;
using ASC.Common.Logging;
using ASC.ElasticSearch;
using ASC.Web.Files.Core.Search;
using ASC.Web.Files.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ASC.Files.Service
{
public class Program
{
public static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostContext, config) =>
{
var buided = config.Build();
var path = buided["pathToConf"];
if (!Path.IsPathRooted(path))
{
path = Path.GetFullPath(Path.Combine(hostContext.HostingEnvironment.ContentRootPath, path));
}
config.SetBasePath(path);
var env = hostContext.Configuration.GetValue("ENVIRONMENT", "Production");
config
.AddInMemoryCollection(new Dictionary<string, string>
{
{"pathToConf", path }
}
)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env}.json", true)
.AddJsonFile($"appsettings.services.json", true)
.AddJsonFile("storage.json")
.AddJsonFile("notify.json")
.AddJsonFile("kafka.json")
.AddJsonFile($"kafka.{env}.json", true)
.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
var diHelper = new DIHelper(services);
diHelper.AddNLogManager("ASC.Files");
services.AddHostedService<ServiceLauncher>();
diHelper
.AddServiceLauncher()
.AddFileConverterService()
.AddKafkaService()
.AddFactoryIndexerFileService()
.AddFactoryIndexerFolderService();
var a = typeof(FactoryIndexer<ISearchItem>).ToString();
services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath, "search.json");
})
.UseConsoleLifetime()
.Build();
using (host)
{
// Start the host
await host.StartAsync();
// Wait for the host to shutdown
await host.WaitForShutdownAsync();
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5009",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"$STORAGE_ROOT": "../../../Data",
"log__name": "files.service",
"log__dir": "../../../Logs"
}
},
"ASC.Files.Service": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:5009",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"$STORAGE_ROOT": "../../../Data",
"log__name": "files.service",
"log__dir": "../../../Logs"
}
}
}
}

View File

@ -0,0 +1,3 @@
{
"pathToConf": "..\\..\\..\\config"
}

View File

@ -0,0 +1,22 @@
{
"components": [
{
"type": "ASC.Web.Files.Core.Search.FactoryIndexerFolder, ASC.Files",
"services": [
{
"type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
}
],
"instanceScope": "perlifetimescope"
},
{
"type": "ASC.Web.Files.Core.Search.FactoryIndexerFile, ASC.Files",
"services": [
{
"type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
}
],
"instanceScope": "perlifetimescope"
}
]
}

View File

@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
namespace AppLimit.CloudComputing.SharpBox.Common.IO
{

View File

@ -1,5 +1,4 @@
using System;
using System.Net;
namespace AppLimit.CloudComputing.SharpBox.Common.Net
{

View File

@ -1,5 +1,4 @@
using System;

using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Impl;
namespace AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Context

View File

@ -1,6 +1,4 @@
using System;
namespace AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Context
namespace AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Context
{
internal class OAuthServiceContext
{

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token;

View File

@ -1,6 +1,4 @@
using System;
namespace AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token
namespace AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token
{
/// <summary>
/// The OAuthToken will be generated during the login process in a oauth compliant

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace AppLimit.CloudComputing.SharpBox
{

View File

@ -1,6 +1,4 @@
using System;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.API
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.API
{
internal class GenericHelper
{

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.API

View File

@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using AppLimit.CloudComputing.SharpBox.Exceptions;
using AppLimit.CloudComputing.SharpBox.StorageProvider.API;
using System.Threading;

View File

@ -1,5 +1,4 @@
using System;
using AppLimit.CloudComputing.SharpBox.StorageProvider.WebDav.Logic;
using AppLimit.CloudComputing.SharpBox.StorageProvider.WebDav.Logic;
using AppLimit.CloudComputing.SharpBox.StorageProvider.API;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.BoxNet.Logic

View File

@ -1,5 +1,4 @@
using System;
using AppLimit.CloudComputing.SharpBox.Common.Net.Json;
using AppLimit.CloudComputing.SharpBox.Common.Net.Json;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox
{

View File

@ -1,6 +1,4 @@
using System;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox
{
/// <summary>
/// The base class of all credentials

View File

@ -1,6 +1,4 @@
using System;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox
{
internal static class DropBoxResourceIDHelpers
{

View File

@ -1,4 +1,3 @@
using System;
using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token;
using AppLimit.CloudComputing.SharpBox.Common.Net.Json;

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Web;
using AppLimit.CloudComputing.SharpBox.Common.Extensions;
using AppLimit.CloudComputing.SharpBox.Common.IO;
using AppLimit.CloudComputing.SharpBox.Common.Net;

View File

@ -1,5 +1,4 @@
using System;
using AppLimit.CloudComputing.SharpBox.StorageProvider.API;
using AppLimit.CloudComputing.SharpBox.StorageProvider.API;
using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Context;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox.Logic

View File

@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
using System.Text.RegularExpressions;
using AppLimit.CloudComputing.SharpBox.StorageProvider.API;
using AppLimit.CloudComputing.SharpBox.StorageProvider.GoogleDocs.Logic;

View File

@ -1,5 +1,4 @@
using System;
using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token;
using AppLimit.CloudComputing.SharpBox.Common.Net.oAuth.Token;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.GoogleDocs
{

View File

@ -1,5 +1,4 @@
using System;
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.SkyDrive
{

View File

@ -1,5 +1,4 @@
using System;
using AppLimit.CloudComputing.SharpBox.StorageProvider.BaseObjects;
using AppLimit.CloudComputing.SharpBox.StorageProvider.BaseObjects;
namespace AppLimit.CloudComputing.SharpBox.StorageProvider.SkyDrive
{

View File

@ -25,10 +25,7 @@
using System;
using ASC.Common.Security.Authentication;
using ASC.Core;
using ASC.Notify.Patterns;
using ASC.Web.Core.Users;
namespace ASC.Web.Studio.Core.Notify
{

View File

@ -43,8 +43,6 @@ using ASC.Core.Common.Settings;
using ASC.Core.Tenants;
using ASC.Data.Storage;
using ASC.Web.Core.Utility.Skins;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Web.Core.Users

View File

@ -105,7 +105,7 @@ namespace ASC.Web.Core
get { return new Guid("{46CFA73A-F320-46CF-8D5B-CD82E1D67F26}"); }
}
public IContainer Container { get; }
public ILifetimeScope Container { get; }
public IConfiguration Configuration { get; }
public IWebItem this[Guid id]
@ -126,8 +126,17 @@ namespace ASC.Web.Core
LoadItems();
}
public WebItemManager(ILifetimeScope container, IConfiguration configuration, IOptionsMonitor<ILog> options)
: this(null, configuration, options)
{
Container = container;
LoadItems();
}
public void LoadItems()
{
if (Container == null) return;
foreach (var webitem in Container.Resolve<IEnumerable<IWebItem>>())
{
var file = webitem.ID.ToString();