diff --git a/ASC.Web.sln b/ASC.Web.sln
index 7b5fd23d0d6..f8af10e8e5f 100644
--- a/ASC.Web.sln
+++ b/ASC.Web.sln
@@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Calendar", "products\AS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.SsoAuth.Svc", "common\services\ASC.SsoAuth.Svc\ASC.SsoAuth.Svc.csproj", "{6AD828EA-FBA2-4D30-B969-756B3BE78E4E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.CRM.BackgroundTasks", "products\ASC.CRM\BackgroundTasks\ASC.CRM.BackgroundTasks.csproj", "{E52C0E35-A05C-437F-8FF2-3E0AC9F81433}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -245,6 +247,10 @@ Global
{6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build/run/CrmBackgroundTasks.bat b/build/run/CrmBackgroundTasks.bat
new file mode 100644
index 00000000000..0e43feef227
--- /dev/null
+++ b/build/run/CrmBackgroundTasks.bat
@@ -0,0 +1,2 @@
+echo "RUN ASC.CRM.BackgroundTasks"
+call dotnet run --project ..\..\products\ASC.CRM\BackgroundTasks\ASC.CRM.BackgroundTasks.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=crm
\ No newline at end of file
diff --git a/build/run/CrmServer.bat b/build/run/CrmServer.bat
index e7b7ed0acd5..ed214856031 100644
--- a/build/run/CrmServer.bat
+++ b/build/run/CrmServer.bat
@@ -1,2 +1,2 @@
-echo "RUN ASC.CRM"
+echo "RUN ASC.CRM.Server"
call dotnet run --project ..\..\products\ASC.CRM\Server\ASC.CRM.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=crm
\ No newline at end of file
diff --git a/common/ASC.Api.Core/ASC.Api.Core.csproj b/common/ASC.Api.Core/ASC.Api.Core.csproj
index a6f4e553ce2..131a6c284bc 100644
--- a/common/ASC.Api.Core/ASC.Api.Core.csproj
+++ b/common/ASC.Api.Core/ASC.Api.Core.csproj
@@ -9,6 +9,10 @@
false
+
+
+
+
diff --git a/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs b/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs
new file mode 100644
index 00000000000..65af908c8b7
--- /dev/null
+++ b/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Linq;
+
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
+
+namespace ASC.Api.Core.Convention
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class ControllerNameAttribute : Attribute
+ {
+ public string Name { get; }
+
+ public ControllerNameAttribute(string name)
+ {
+ Name = name;
+ }
+ }
+
+ public class ControllerNameAttributeConvention : IControllerModelConvention
+ {
+ public void Apply(ControllerModel controller)
+ {
+ var controllerNameAttribute = controller.Attributes.OfType().SingleOrDefault();
+ if (controllerNameAttribute != null)
+ {
+ controller.ControllerName = controllerNameAttribute.Name;
+ }
+ }
+ }
+}
diff --git a/common/ASC.Api.Core/Core/BaseStartup.cs b/common/ASC.Api.Core/Core/BaseStartup.cs
index 1146255bfa9..b81527c2e95 100644
--- a/common/ASC.Api.Core/Core/BaseStartup.cs
+++ b/common/ASC.Api.Core/Core/BaseStartup.cs
@@ -1,12 +1,15 @@
-using System.Text.Json.Serialization;
+using System.Reflection;
+using System.Text.Json.Serialization;
using ASC.Api.Core.Auth;
+using ASC.Api.Core.Convention;
using ASC.Api.Core.Core;
using ASC.Api.Core.Middleware;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.DependencyInjection;
using ASC.Common.Logging;
+using ASC.Common.Mapping;
using Autofac;
@@ -81,13 +84,16 @@ public virtual void ConfigureServices(IServiceCollection services)
DIHelper.RegisterProducts(Configuration, HostEnvironment.ContentRootPath);
var builder = services.AddMvcCore(config =>
- {
- var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
- config.Filters.Add(new AuthorizeFilter(policy));
- config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter)));
- config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter)));
- config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter)));
- config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter)));
+ {
+ config.Conventions.Add(new ControllerNameAttributeConvention());
+
+ var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
+
+ config.Filters.Add(new AuthorizeFilter(policy));
+ config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter)));
+ config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter)));
+ config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter)));
+ config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter)));
config.Filters.Add(new CustomResponseFilterAttribute());
config.Filters.Add(new CustomExceptionFilterAttribute());
config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter)));
@@ -108,7 +114,9 @@ public virtual void ConfigureServices(IServiceCollection services)
if (LogParams != null)
{
LogNLogExtension.ConfigureLog(DIHelper, LogParams);
- }
+ }
+
+ services.AddAutoMapper(Assembly.GetAssembly(typeof(MappingProfile)));
}
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
diff --git a/common/ASC.Common/ASC.Common.csproj b/common/ASC.Common/ASC.Common.csproj
index c0330ac2cf2..51385aa1c2c 100644
--- a/common/ASC.Common/ASC.Common.csproj
+++ b/common/ASC.Common/ASC.Common.csproj
@@ -29,6 +29,7 @@
+
diff --git a/common/ASC.Common/Mapping/IMapFrom.cs b/common/ASC.Common/Mapping/IMapFrom.cs
new file mode 100644
index 00000000000..3be8426f161
--- /dev/null
+++ b/common/ASC.Common/Mapping/IMapFrom.cs
@@ -0,0 +1,34 @@
+/*
+ *
+ * (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 AutoMapper;
+
+namespace ASC.Common.Mapping
+{
+ public interface IMapFrom
+ {
+ void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
+ }
+}
diff --git a/common/ASC.Common/Mapping/MappingProfile.cs b/common/ASC.Common/Mapping/MappingProfile.cs
new file mode 100644
index 00000000000..3d16f3f2282
--- /dev/null
+++ b/common/ASC.Common/Mapping/MappingProfile.cs
@@ -0,0 +1,62 @@
+/*
+ *
+ * (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.Linq;
+using System.Reflection;
+
+using AutoMapper;
+
+namespace ASC.Common.Mapping
+{
+ public class MappingProfile : Profile
+ {
+ public MappingProfile()
+ {
+ Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a));
+ }
+
+ private void ApplyMappingsFromAssembly(Assembly assembly)
+ {
+ if (!assembly.GetName().Name.StartsWith("ASC.")) return;
+
+ var types = assembly.GetExportedTypes()
+ .Where(t => t.GetInterfaces().Any(i =>
+ i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
+ .ToList();
+
+ foreach (var type in types)
+ {
+ var instance = Activator.CreateInstance(type);
+
+ var methodInfo = type.GetMethod("Mapping")
+ ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
+
+ methodInfo?.Invoke(instance, new object[] { this });
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/autofac.products.json b/config/autofac.products.json
index a14fc90734a..d1b91e2111d 100644
--- a/config/autofac.products.json
+++ b/config/autofac.products.json
@@ -28,7 +28,7 @@
"instanceScope": "InstancePerLifetimeScope"
},
{
- "type": "ASC.CRM.Configuration.ProductEntryPoint, ASC.CRM",
+ "type": "ASC.Web.CRM.Configuration.ProductEntryPoint, ASC.CRM",
"services": [
{
"type": "ASC.Web.Core.IWebItem, ASC.Web.Core"
diff --git a/config/nlog.config b/config/nlog.config
index 28fd9b092e1..d208716642a 100644
--- a/config/nlog.config
+++ b/config/nlog.config
@@ -20,6 +20,6 @@
-
+
\ No newline at end of file
diff --git a/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj b/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj
new file mode 100644
index 00000000000..27569b88aea
--- /dev/null
+++ b/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj
@@ -0,0 +1,8 @@
+
+
+ net5.0
+
+
+
+
+
diff --git a/products/ASC.CRM/BackgroundTasks/Program.cs b/products/ASC.CRM/BackgroundTasks/Program.cs
new file mode 100644
index 00000000000..5445498c51a
--- /dev/null
+++ b/products/ASC.CRM/BackgroundTasks/Program.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+using ASC.Common;
+using ASC.Common.Caching;
+using ASC.Common.Logging;
+using ASC.Common.Utils;
+using ASC.ElasticSearch;
+using ASC.Web.CRM.Core.Search;
+
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using ASC.Common.DependencyInjection;
+
+
+namespace ASC.CRM.BackgroundTasks
+{
+ class Program
+ {
+ public async static Task Main(string[] args)
+ {
+ var host = CreateHostBuilder(args).Build();
+
+ await host.RunAsync();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .UseSystemd()
+ .UseWindowsService()
+ .UseServiceProviderFactory(new AutofacServiceProviderFactory())
+ .ConfigureAppConfiguration((hostContext, config) =>
+ {
+ var buided = config.Build();
+ var path = buided["pathToConf"];
+ if (!Path.IsPathRooted(path))
+ {
+ path = Path.GetFullPath(CrossPlatform.PathCombine(hostContext.HostingEnvironment.ContentRootPath, path));
+ }
+ config.SetBasePath(path);
+ var env = hostContext.Configuration.GetValue("ENVIRONMENT", "Production");
+ config
+ .AddInMemoryCollection(new Dictionary
+ {
+ {"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()
+ .AddCommandLine(args);
+ })
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddMemoryCache();
+
+ var diHelper = new DIHelper(services);
+
+ diHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCache<>));
+
+ diHelper.RegisterProducts(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath);
+
+ services.AddHostedService();
+ diHelper.TryAdd();
+
+ LogNLogExtension.ConfigureLog(diHelper, "ASC.Files", "ASC.Feed.Agregator");
+
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ diHelper.TryAdd();
+ })
+ .ConfigureContainer((context, builder) =>
+ {
+ builder.Register(context.Configuration, true, false, "search.json");
+ });
+ }
+}
diff --git a/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json b/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json
new file mode 100644
index 00000000000..cad5cdec51b
--- /dev/null
+++ b/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "profiles": {
+ "Kestrel WebServer": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "$STORAGE_ROOT": "../../../Data",
+ "log__dir": "../../../Logs",
+ "log__name": "crm",
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:5026"
+ }
+ },
+ "WSL 2 : Ubuntu 20.04": {
+ "commandName": "WSL2",
+ "launchBrowser": false,
+ "environmentVariables": {
+ "$STORAGE_ROOT": "../../../Data",
+ "log__dir": "../../../Logs",
+ "log__name": "crm",
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:5026"
+ },
+ "distributionName": "Ubuntu-20.04"
+ }
+ }
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/BackgroundTasks/appsettings.json b/products/ASC.CRM/BackgroundTasks/appsettings.json
new file mode 100644
index 00000000000..9bf702e3108
--- /dev/null
+++ b/products/ASC.CRM/BackgroundTasks/appsettings.json
@@ -0,0 +1,3 @@
+{
+ "pathToConf": "..\\..\\..\\config"
+}
diff --git a/products/ASC.CRM/BackgroundTasks/search.json b/products/ASC.CRM/BackgroundTasks/search.json
new file mode 100644
index 00000000000..2138b66a234
--- /dev/null
+++ b/products/ASC.CRM/BackgroundTasks/search.json
@@ -0,0 +1,76 @@
+{
+ "components": [
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerCase, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerContact, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerContactInfo, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerDeal, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerEvents, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerFieldValue, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerInvoice, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ },
+ {
+ "type": "ASC.Web.CRM.Core.Search.FactoryIndexerTask, ASC.CRM",
+ "services": [
+ {
+ "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch"
+ }
+ ],
+ "instanceScope": "perlifetimescope"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/Server/ASC.CRM.csproj b/products/ASC.CRM/Server/ASC.CRM.csproj
index 551a8d35579..af1f4be2857 100644
--- a/products/ASC.CRM/Server/ASC.CRM.csproj
+++ b/products/ASC.CRM/Server/ASC.CRM.csproj
@@ -1,46 +1,192 @@
-
-
-
- net5.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- CRMCommonResource.resx
-
-
-
-
-
- ResXFileCodeGenerator
- CRMCommonResource.Designer.cs
-
-
- CRMCommonResource.resx
-
-
- CRMCommonResource.resx
-
-
- CRMCommonResource.resx
-
-
- CRMCommonResource.resx
-
-
- CRMCommonResource.resx
-
-
-
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ CRMCasesResource.resx
+
+
+ True
+ True
+ CRMCommonResource.resx
+
+
+ True
+ True
+ CRMContactResource.resx
+
+
+ True
+ True
+ CRMDealResource.resx
+
+
+ True
+ True
+ CRMEnumResource.resx
+
+
+ True
+ True
+ CRMErrorsResource.resx
+
+
+ True
+ True
+ CRMInvoiceResource.resx
+
+
+ True
+ True
+ CRMJSResource.resx
+
+
+ True
+ True
+ CRMReportResource.resx
+
+
+ True
+ True
+ CRMSettingResource.resx
+
+
+ True
+ True
+ CRMSocialMediaResource.resx
+
+
+ True
+ True
+ CRMTaskResource.resx
+
+
+ True
+ True
+ CRMVoipResource.resx
+
+
+ True
+ True
+ CRMPatternResource.resx
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ CRMCasesResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMCommonResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMContactResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMDealResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMEnumResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMErrorsResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMInvoiceResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMJSResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMReportResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMSettingResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMSocialMediaResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMTaskResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMVoipResource.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ CRMPatternResource.Designer.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/products/ASC.CRM/Server/Api/BaseApiController.cs b/products/ASC.CRM/Server/Api/BaseApiController.cs
new file mode 100644
index 00000000000..b4130f0f3e4
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/BaseApiController.cs
@@ -0,0 +1,121 @@
+/*
+ *
+ * (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.Api.Core.Convention;
+using ASC.Common;
+using ASC.Common.Web;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Core.Entities;
+using ASC.CRM.Core.Enums;
+using ASC.Web.Api.Routing;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace ASC.Api.CRM
+{
+ [Scope]
+ [DefaultRoute]
+ [ApiController]
+ [ControllerName("crm")]
+ public abstract class BaseApiController : ControllerBase
+ {
+ protected IMapper _mapper;
+ protected DaoFactory _daoFactory;
+ protected CrmSecurity _crmSecurity;
+
+ public BaseApiController(DaoFactory daoFactory,
+ CrmSecurity crmSecurity,
+ IMapper mapper)
+ {
+ _daoFactory = daoFactory;
+ _crmSecurity = crmSecurity;
+ _mapper = mapper;
+ }
+
+
+ protected static EntityType ToEntityType(string entityTypeStr)
+ {
+ EntityType entityType;
+
+ if (string.IsNullOrEmpty(entityTypeStr)) return EntityType.Any;
+
+ switch (entityTypeStr.ToLower())
+ {
+ case "person":
+ entityType = EntityType.Person;
+ break;
+ case "company":
+ entityType = EntityType.Company;
+ break;
+ case "contact":
+ entityType = EntityType.Contact;
+ break;
+ case "opportunity":
+ entityType = EntityType.Opportunity;
+ break;
+ case "case":
+ entityType = EntityType.Case;
+ break;
+ default:
+ entityType = EntityType.Any;
+ break;
+ }
+
+ return entityType;
+ }
+
+ protected string GetEntityTitle(EntityType entityType, int entityId, bool checkAccess, out DomainObject entity)
+ {
+ switch (entityType)
+ {
+ case EntityType.Contact:
+ case EntityType.Company:
+ case EntityType.Person:
+ var contact = (entity = _daoFactory.GetContactDao().GetByID(entityId)) as ASC.CRM.Core.Entities.Contact;
+ if (contact == null || (checkAccess && !_crmSecurity.CanAccessTo(contact)))
+ throw new ItemNotFoundException();
+ return contact.GetTitle();
+ case EntityType.Opportunity:
+ var deal = (entity = _daoFactory.GetDealDao().GetByID(entityId)) as Deal;
+ if (deal == null || (checkAccess && !_crmSecurity.CanAccessTo(deal)))
+ throw new ItemNotFoundException();
+ return deal.Title;
+ case EntityType.Case:
+ var cases = (entity = _daoFactory.GetCasesDao().GetByID(entityId)) as Cases;
+ if (cases == null || (checkAccess && !_crmSecurity.CanAccessTo(cases)))
+ throw new ItemNotFoundException();
+ return cases.Title;
+ default:
+ throw new ArgumentException("Invalid entityType: " + entityType);
+ }
+ }
+
+ }
+}
diff --git a/products/ASC.CRM/Server/Api/CasesController.cs b/products/ASC.CRM/Server/Api/CasesController.cs
new file mode 100644
index 00000000000..a74a02454c6
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/CasesController.cs
@@ -0,0 +1,781 @@
+/*
+ *
+ * (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.Api.Core;
+using ASC.Api.CRM;
+using ASC.Common.Web;
+using ASC.Core;
+using ASC.Core.Users;
+using ASC.CRM.ApiModels;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Core.Entities;
+using ASC.CRM.Core.Enums;
+using ASC.ElasticSearch;
+using ASC.MessagingSystem;
+using ASC.Web.Api.Routing;
+using ASC.Web.Core.Users;
+using ASC.Web.CRM.Services.NotifyService;
+using ASC.Web.Files.Services.WCFService;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace ASC.CRM.Api
+{
+ public class CasesController : BaseApiController
+ {
+ private readonly DisplayUserSettingsHelper _displayUserSettingsHelper;
+ private readonly SecurityContext _securityContext;
+ private readonly NotifyClient _notifyClient;
+ private readonly ApiContext _apiContext;
+ private readonly MessageService _messageService;
+ private readonly MessageTarget _messageTarget;
+ private readonly UserManager _userManager;
+
+ public CasesController(CrmSecurity crmSecurity,
+ DaoFactory daoFactory,
+ ApiContext apiContext,
+ MessageTarget messageTarget,
+ MessageService messageService,
+ NotifyClient notifyClient,
+ SecurityContext securityContext,
+ DisplayUserSettingsHelper displayUserSettingsHelper,
+ UserManager userManager,
+ IMapper mapper)
+ : base(daoFactory, crmSecurity, mapper)
+ {
+ _apiContext = apiContext;
+ _messageTarget = messageTarget;
+ _messageService = messageService;
+ _notifyClient = notifyClient;
+ _securityContext = securityContext;
+ _displayUserSettingsHelper = displayUserSettingsHelper;
+ _userManager = userManager;
+ _mapper = mapper;
+ }
+
+
+ ///
+ /// Close the case with the ID specified in the request
+ ///
+ /// Close case
+ /// Cases
+ /// Case ID
+ ///
+ ///
+ ///
+ /// Case
+ ///
+ [Update(@"case/{caseid:int}/close")]
+ public CasesDto CloseCases(int caseid)
+ {
+ if (caseid <= 0) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().CloseCases(caseid);
+ if (cases == null) throw new ItemNotFoundException();
+
+ _messageService.Send(MessageAction.CaseClosed, _messageTarget.Create(cases.ID), cases.Title);
+
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Resume the case with the ID specified in the request
+ ///
+ /// Resume case
+ /// Cases
+ /// Case ID
+ ///
+ ///
+ ///
+ /// Case
+ ///
+ [Update(@"case/{caseid:int}/reopen")]
+ public CasesDto ReOpenCases(int caseid)
+ {
+ if (caseid <= 0) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().ReOpenCases(caseid);
+ if (cases == null) throw new ItemNotFoundException();
+
+ _messageService.Send(MessageAction.CaseOpened, _messageTarget.Create(cases.ID), cases.Title);
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Creates the case with the parameters specified in the request
+ ///
+ /// Create case
+ /// Case title
+ /// Participants
+ /// User field list
+ /// Case privacy: private or not
+ /// List of users with access to the case
+ /// Notify users in accessList about the case
+ /// Case
+ /// Cases
+ ///
+ ///
+ ///
+ ///
+ [Create(@"case")]
+ public CasesDto CreateCases([FromForm] CreateOrUpdateCasesRequestDto inDto)
+ {
+
+ var title = inDto.Title;
+ var customFieldList = inDto.CustomFieldList;
+ var isPrivate = inDto.isPrivate;
+ var isNotify = inDto.isNotify;
+ var members = inDto.Members;
+ var accessList = inDto.accessList;
+
+ if (string.IsNullOrEmpty(title)) throw new ArgumentException();
+
+ var casesID = _daoFactory.GetCasesDao().CreateCases(title);
+
+ var cases = new Cases
+ {
+ ID = casesID,
+ Title = title,
+ CreateBy = _securityContext.CurrentAccount.ID,
+ CreateOn = DateTime.UtcNow
+ };
+
+ SetAccessToCases(cases, isPrivate, accessList, isNotify, false);
+
+ var membersList = members != null ? members.ToList() : new List();
+ if (membersList.Any())
+ {
+ var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList();
+ membersList = contacts.Select(m => m.ID).ToList();
+ _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray());
+ }
+
+ if (customFieldList != null)
+ {
+ var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList();
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value);
+ }
+ }
+
+ var casesToResult = _daoFactory.GetCasesDao().GetByID(casesID);
+
+ return _mapper.Map(casesToResult);
+ }
+
+ ///
+ /// Updates the selected case with the parameters specified in the request
+ ///
+ /// Update case
+ /// Case ID
+ /// Case title
+ /// Participants
+ /// User field list
+ /// Case privacy: private or not
+ /// List of users with access to the case
+ /// Notify users in accessList about the case
+ /// Cases
+ /// Case
+ ///
+ ///
+ ///
+ ///
+ ///
+ [Update(@"case/{caseid:int}")]
+ public CasesDto UpdateCases(int caseid, [FromForm] CreateOrUpdateCasesRequestDto inDto)
+ {
+ var title = inDto.Title;
+ var isPrivate = inDto.isPrivate;
+ var isNotify = inDto.isNotify;
+ var accessList = inDto.accessList;
+ var members = inDto.Members;
+ var customFieldList = inDto.CustomFieldList;
+
+ if ((caseid <= 0) || (string.IsNullOrEmpty(title))) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().GetByID(caseid);
+ if (cases == null) throw new ItemNotFoundException();
+
+ cases.Title = title;
+
+ _daoFactory.GetCasesDao().UpdateCases(cases);
+
+ if (_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID)
+ {
+ SetAccessToCases(cases, isPrivate, accessList, isNotify, false);
+ }
+
+ var membersList = members != null ? members.ToList() : new List();
+ if (membersList.Any())
+ {
+ var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList();
+ membersList = contacts.Select(m => m.ID).ToList();
+ _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray());
+ }
+
+ if (customFieldList != null)
+ {
+ var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList();
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value);
+ }
+ }
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Sets access rights for the selected case with the parameters specified in the request
+ ///
+ /// Case ID
+ /// Case privacy: private or not
+ /// List of users with access to the case
+ /// Set rights to case
+ /// Cases
+ ///
+ ///
+ ///
+ /// Case
+ ///
+ [Update(@"case/{caseid:int}/access")]
+ public CasesDto SetAccessToCases(int caseid, bool isPrivate, IEnumerable accessList)
+ {
+ if (caseid <= 0) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().GetByID(caseid);
+ if (cases == null) throw new ItemNotFoundException();
+
+ if (!(_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID)) throw _crmSecurity.CreateSecurityException();
+
+ return SetAccessToCases(cases, isPrivate, accessList, false, true);
+ }
+
+ private CasesDto SetAccessToCases(Cases cases, bool isPrivate, IEnumerable accessList, bool isNotify, bool isMessageServicSende)
+ {
+ var accessListLocal = accessList != null ? accessList.Distinct().ToList() : new List();
+ if (isPrivate && accessListLocal.Any())
+ {
+ if (isNotify)
+ {
+ accessListLocal = accessListLocal.Where(u => u != _securityContext.CurrentAccount.ID).ToList();
+ _notifyClient.SendAboutSetAccess(EntityType.Case, cases.ID, _daoFactory, accessListLocal.ToArray());
+ }
+
+ if (!accessListLocal.Contains(_securityContext.CurrentAccount.ID))
+ {
+ accessListLocal.Add(_securityContext.CurrentAccount.ID);
+ }
+
+ _crmSecurity.SetAccessTo(cases, accessListLocal);
+ if (isMessageServicSende)
+ {
+ var users = GetUsersByIdList(accessListLocal);
+ _messageService.Send(MessageAction.CaseRestrictedAccess, _messageTarget.Create(cases.ID), cases.Title, users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper)));
+ }
+ }
+ else
+ {
+ _crmSecurity.MakePublic(cases);
+ if (isMessageServicSende)
+ {
+ _messageService.Send(MessageAction.CaseOpenedAccess, _messageTarget.Create(cases.ID), cases.Title);
+ }
+ }
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Sets access rights for other users to the list of cases with the IDs specified in the request
+ ///
+ /// Case ID list
+ /// Case privacy: private or not
+ /// List of users with access
+ /// Set case access rights
+ /// Cases
+ ///
+ ///
+ ///
+ /// Case list
+ ///
+ [Update(@"case/access")]
+ public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesRequestDto inDto)
+ {
+ var casesid = inDto.CasesId;
+ var isPrivate = inDto.isPrivate;
+ var accessList = inDto.AccessList;
+
+ var result = new List();
+
+ var cases = _daoFactory.GetCasesDao().GetCases(casesid);
+
+ if (!cases.Any()) return new List();
+
+ foreach (var c in cases)
+ {
+ if (c == null) throw new ItemNotFoundException();
+
+ if (!(_crmSecurity.IsAdmin || c.CreateBy == _securityContext.CurrentAccount.ID)) continue;
+
+ SetAccessToCases(c, isPrivate, accessList, false, true);
+ result.Add(c);
+ }
+
+ return ToListCasesDtos(result);
+ }
+
+ ///
+ /// Sets access rights for other users to the list of all cases matching the parameters specified in the request
+ ///
+ /// Contact ID
+ /// Case status
+ /// Tags
+ /// Case privacy: private or not
+ /// List of users with access
+ /// Set case access rights
+ /// Cases
+ ///
+ ///
+ ///
+ /// Case list
+ ///
+ [Update(@"case/filter/access")]
+ public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesByFilterInDto inDto)
+ {
+ int contactid = inDto.Contactid;
+ bool? isClosed = inDto.isClosed;
+ IEnumerable tags = inDto.Tags;
+ bool isPrivate = inDto.isPrivate;
+ IEnumerable accessList = inDto.AccessList;
+
+ var result = new List();
+
+ var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null);
+
+ if (!caseses.Any()) return new List();
+
+ foreach (var casese in caseses)
+ {
+ if (casese == null) throw new ItemNotFoundException();
+
+ if (!(_crmSecurity.IsAdmin || casese.CreateBy == _securityContext.CurrentAccount.ID)) continue;
+
+ SetAccessToCases(casese, isPrivate, accessList, false, true);
+ result.Add(casese);
+ }
+
+ return ToListCasesDtos(result);
+ }
+
+ ///
+ /// Returns the detailed information about the case with the ID specified in the request
+ ///
+ /// Get case by ID
+ /// Cases
+ /// Case ID
+ ///
+ ///
+ [Read(@"case/{caseid:int}")]
+ public CasesDto GetCaseByID(int caseid)
+ {
+ if (caseid <= 0) throw new ItemNotFoundException();
+
+ var cases = _daoFactory.GetCasesDao().GetByID(caseid);
+ if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException();
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Returns the list of all cases matching the parameters specified in the request
+ ///
+ /// Get case list
+ /// Contact ID
+ /// Case status
+ /// Tags
+ /// Cases
+ ///
+ /// Case list
+ ///
+ [Read(@"case/filter")]
+ public IEnumerable GetCases(int contactid, bool? isClosed, IEnumerable tags)
+ {
+ IEnumerable result;
+ SortedByType sortBy;
+ OrderBy casesOrderBy;
+
+ var searchString = _apiContext.FilterValue;
+
+ if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy))
+ {
+ casesOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending);
+ }
+ else if (string.IsNullOrEmpty(_apiContext.SortBy))
+ {
+ casesOrderBy = new OrderBy(SortedByType.Title, true);
+ }
+ else
+ {
+ casesOrderBy = null;
+ }
+
+ var fromIndex = (int)_apiContext.StartIndex;
+ var count = (int)_apiContext.Count;
+
+ if (casesOrderBy != null)
+ {
+ result = ToListCasesDtos(
+ _daoFactory
+ .GetCasesDao()
+ .GetCases(
+ searchString,
+ contactid,
+ isClosed,
+ tags,
+ fromIndex,
+ count,
+ casesOrderBy)).ToList();
+
+ _apiContext.SetDataPaginated();
+ _apiContext.SetDataFiltered();
+ _apiContext.SetDataSorted();
+ }
+ else
+ {
+ result = ToListCasesDtos(
+ _daoFactory
+ .GetCasesDao()
+ .GetCases(
+ searchString, contactid, isClosed,
+ tags,
+ 0,
+ 0,
+ null)).ToList();
+ }
+
+ int totalCount;
+
+ if (result.Count() < count)
+ {
+ totalCount = fromIndex + result.Count();
+ }
+ else
+ {
+ totalCount = _daoFactory.GetCasesDao().GetCasesCount(searchString, contactid, isClosed, tags);
+ }
+
+ _apiContext.SetTotalCount(totalCount);
+ return result;
+ }
+
+ ///
+ /// Deletes the case with the ID specified in the request
+ ///
+ /// Delete case
+ /// Case ID
+ /// Cases
+ ///
+ ///
+ ///
+ /// Case
+ ///
+ [Delete(@"case/{caseid:int}")]
+ public CasesDto DeleteCase(int caseid)
+ {
+ if (caseid <= 0) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().DeleteCases(caseid);
+
+ if (cases == null) throw new ItemNotFoundException();
+
+ _messageService.Send(MessageAction.CaseDeleted, _messageTarget.Create(cases.ID), cases.Title);
+
+ return _mapper.Map(cases);
+ }
+
+ ///
+ /// Deletes the group of cases with the IDs specified in the request
+ ///
+ /// Case ID list
+ ///
+ ///
+ /// Delete case group
+ /// Cases
+ ///
+ /// Case list
+ ///
+ [Update(@"case")]
+ public IEnumerable DeleteBatchCases([FromForm] IEnumerable casesids)
+ {
+ if (casesids == null) throw new ArgumentException();
+
+ casesids = casesids.Distinct();
+ var caseses = _daoFactory.GetCasesDao().DeleteBatchCases(casesids.ToArray());
+
+ if (caseses == null || !caseses.Any()) return new List();
+
+ _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(casesids), caseses.Select(c => c.Title));
+
+ return ToListCasesDtos(caseses);
+ }
+
+ ///
+ /// Deletes the list of all cases matching the parameters specified in the request
+ ///
+ /// Contact ID
+ /// Case status
+ /// Tags
+ ///
+ ///
+ /// Delete case group
+ /// Cases
+ ///
+ /// Case list
+ ///
+ [Delete(@"case/filter")]
+ public IEnumerable DeleteBatchCases(int contactid, bool? isClosed, IEnumerable tags)
+ {
+ var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null);
+ if (!caseses.Any()) return new List();
+
+ caseses = _daoFactory.GetCasesDao().DeleteBatchCases(caseses);
+
+ _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(caseses.Select(c => c.ID)), caseses.Select(c => c.Title));
+
+ return ToListCasesDtos(caseses);
+ }
+
+ ///
+ /// Returns the list of all contacts associated with the case with the ID specified in the request
+ ///
+ /// Get all case contacts
+ /// Case ID
+ /// Cases
+ /// Contact list
+ ///
+ [Read(@"case/{caseid:int}/contact")]
+ public IEnumerable GetCasesMembers(int caseid)
+ {
+ var contactIDs = _daoFactory.GetCasesDao().GetMembers(caseid);
+ var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs);
+
+ return contactIDs == null
+ ? new ItemList()
+ : _mapper.Map, List>(contacts);
+ }
+
+ ///
+ /// Adds the selected contact to the case with the ID specified in the request
+ ///
+ /// Add case contact
+ /// Cases
+ /// Case ID
+ /// Contact ID
+ ///
+ ///
+ ///
+ /// Participant
+ ///
+ [Create(@"case/{caseid:int}/contact")]
+ public ContactDto AddMemberToCases([FromRoute] int caseid, [FromForm] int contactid)
+ {
+ if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().GetByID(caseid);
+ if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ _daoFactory.GetCasesDao().AddMember(caseid, contactid);
+
+ var messageAction = contact is Company ? MessageAction.CaseLinkedCompany : MessageAction.CaseLinkedPerson;
+ _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle());
+
+ return _mapper.Map(contact);
+ }
+
+ ///
+ /// Delete the selected contact from the case with the ID specified in the request
+ ///
+ /// Delete case contact
+ /// Cases
+ /// Case ID
+ /// Contact ID
+ ///
+ ///
+ ///
+ /// Participant
+ ///
+ [Delete(@"case/{caseid:int}/contact/{contactid:int}")]
+ public ContactDto DeleteMemberFromCases(int caseid, int contactid)
+ {
+ if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException();
+
+ var cases = _daoFactory.GetCasesDao().GetByID(caseid);
+ if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ var result = _mapper.Map(contact);
+
+ _daoFactory.GetCasesDao().RemoveMember(caseid, contactid);
+
+ var messageAction = contact is Company ? MessageAction.CaseUnlinkedCompany : MessageAction.CaseUnlinkedPerson;
+
+ _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle());
+
+ return result;
+ }
+
+ ///
+ /// Returns the list of 30 cases in the CRM module with prefix
+ ///
+ ///
+ ///
+ /// Cases
+ ///
+ /// Cases list
+ ///
+ /// false
+ [Read(@"case/byprefix")]
+ public IEnumerable GetCasesByPrefix(string prefix, int contactID)
+ {
+ var result = new List();
+
+ if (contactID > 0)
+ {
+ var findedCases = _daoFactory.GetCasesDao().GetCases(string.Empty, contactID, null, null, 0, 0, null);
+
+ foreach (var item in findedCases)
+ {
+ if (item.Title.IndexOf(prefix, StringComparison.Ordinal) != -1)
+ {
+ result.Add(_mapper.Map(item));
+ }
+ }
+
+ _apiContext.SetTotalCount(findedCases.Count);
+ }
+ else
+ {
+ const int maxItemCount = 30;
+ var findedCases = _daoFactory.GetCasesDao().GetCasesByPrefix(prefix, 0, maxItemCount);
+
+ foreach (var item in findedCases)
+ {
+ result.Add(_mapper.Map(item));
+ }
+ }
+
+ return result;
+ }
+
+ private IEnumerable ToListCasesDtos(ICollection items)
+ {
+ if (items == null || items.Count == 0) return new List();
+
+ var result = new List();
+
+ var contactIDs = new List();
+ var casesIDs = items.Select(item => item.ID).ToArray();
+
+ var customFields = _daoFactory.GetCustomFieldDao()
+ .GetEnityFields(EntityType.Case, casesIDs)
+ .GroupBy(item => item.EntityID)
+ .ToDictionary(item => item.Key, item => item.Select(x => _mapper.Map(x)));
+
+ var casesMembers = _daoFactory.GetCasesDao().GetMembers(casesIDs);
+
+ foreach (var value in casesMembers.Values)
+ {
+ contactIDs.AddRange(value);
+ }
+
+ var contacts = _daoFactory
+ .GetContactDao()
+ .GetContacts(contactIDs.Distinct().ToArray())
+ .ToDictionary(item => item.ID, x => _mapper.Map(x));
+
+ foreach (var cases in items)
+ {
+ var casesDto = _mapper.Map(cases);
+
+ casesDto.CustomFields = customFields.ContainsKey(cases.ID)
+ ? customFields[cases.ID]
+ : new List();
+
+ casesDto.Members = casesMembers.ContainsKey(cases.ID)
+ ? casesMembers[cases.ID].Where(contacts.ContainsKey).Select(item => contacts[item])
+ : new List();
+
+ result.Add(casesDto);
+ }
+
+ return result;
+ }
+
+ private IEnumerable GetUsersByIdList(IEnumerable ids)
+ {
+ return _userManager.GetUsers().Where(x => ids.Contains(x.ID));
+ }
+ }
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/Server/Api/ContactInfosController.cs b/products/ASC.CRM/Server/Api/ContactInfosController.cs
new file mode 100644
index 00000000000..7314d0ab646
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/ContactInfosController.cs
@@ -0,0 +1,549 @@
+/*
+ *
+ * (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 System.Text.Json;
+
+using ASC.Api.CRM;
+using ASC.Common.Web;
+using ASC.CRM.ApiModels;
+using ASC.CRM.Classes;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Core.Entities;
+using ASC.CRM.Core.Enums;
+using ASC.MessagingSystem;
+using ASC.Web.Api.Routing;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace ASC.CRM.Api
+{
+ public class ContactInfosController : BaseApiController
+ {
+ private readonly MessageService _messageService;
+ private readonly MessageTarget _messageTarget;
+
+ public ContactInfosController(CrmSecurity crmSecurity,
+ DaoFactory daoFactory,
+ MessageTarget messageTarget,
+ MessageService messageService,
+ IMapper mapper)
+ : base(daoFactory, crmSecurity, mapper)
+ {
+ _messageTarget = messageTarget;
+ _messageService = messageService;
+ }
+
+
+ ///
+ /// Returns the list of all available contact categories
+ ///
+ ///
+ /// Contact information type
+ ///
+ /// Get all categories
+ /// Contacts
+ ///
+ /// List of all available contact categories
+ ///
+ [Read(@"contact/data/{infoType}/category")]
+ public IEnumerable GetContactInfoCategory(ContactInfoType infoType)
+ {
+ return Enum.GetNames(ContactInfo.GetCategory(infoType));
+ }
+
+ ///
+ /// Returns the list of all available contact information types
+ ///
+ /// Get all contact info types
+ /// Contacts
+ ///
+ [Read(@"contact/data/infoType")]
+ public IEnumerable GetContactInfoType()
+ {
+ return Enum.GetNames(typeof(ContactInfoType));
+ }
+
+ ///
+ /// Returns the detailed information for the contact
+ ///
+ /// Contact ID
+ /// Get contact information
+ /// Contacts
+ ///
+ /// Contact information
+ ///
+ [Read(@"contact/{contactid:int}/data")]
+ public IEnumerable GetContactInfo(int contactid)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ var sourceData = _daoFactory.GetContactInfoDao().GetList(contactid, null, null, null)
+ .OrderByDescending(info => info.ID)
+ .ToList();
+
+ return _mapper.Map, List>(sourceData);
+ }
+
+ ///
+ /// Returns the detailed list of all information available for the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// Contact information ID
+ /// Get contact info
+ /// Contacts
+ /// Contact information
+ ///
+ [Read(@"contact/{contactid:int}/data/{id:int}")]
+ public ContactInfoDto GetContactInfoByID(int contactid, int id)
+ {
+ if (contactid <= 0 || id <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id);
+
+ if (contactInfo == null || contactInfo.ContactID != contactid) throw new ArgumentException();
+
+ return _mapper.Map(contactInfo);
+ }
+
+ ///
+ /// Adds the information with the parameters specified in the request to the contact with the selected ID
+ ///
+ ///Contact ID
+ ///Contact information type
+ ///Data
+ ///Contact importance: primary or not
+ ///Category
+ /// Add contact info
+ ///Contacts
+ ///
+ ///
+ ///
+ /// Contact information
+ ///
+ ///
+ [Create(@"contact/{contactid:int}/data")]
+ public ContactInfoDto CreateContactInfo(
+ [FromRoute] int contactid,
+ [FromForm] ContactInfoType infoType,
+ [FromForm] string data,
+ [FromForm] bool isPrimary,
+ [FromForm] string category)
+ {
+ if (string.IsNullOrEmpty(data) || contactid <= 0) throw new ArgumentException();
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null) throw new ItemNotFoundException();
+
+ if (infoType == ContactInfoType.Twitter)
+ {
+ if (!_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+ }
+ else
+ {
+ if (!_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+ }
+
+ var categoryType = ContactInfo.GetCategory(infoType);
+ if (!Enum.IsDefined(categoryType, category)) throw new ArgumentException();
+
+
+ var contactInfo = new ContactInfo
+ {
+ Data = data,
+ InfoType = infoType,
+ ContactID = contactid,
+ IsPrimary = isPrimary,
+ Category = (int)Enum.Parse(categoryType, category)
+ };
+
+ if (contactInfo.InfoType == ContactInfoType.Address)
+ {
+ Address res;
+ if (!Address.TryParse(contactInfo, out res))
+ throw new ArgumentException();
+ }
+
+ var contactInfoID = _daoFactory.GetContactInfoDao().Save(contactInfo);
+
+ var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo;
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ var contactInfoDto = _mapper.Map(contactInfo);
+
+ contactInfoDto.Id = contactInfoID;
+
+ return contactInfoDto;
+ }
+
+ ///
+ /// Adds the address information to the contact with the selected ID
+ ///
+ /// Contact ID
+ /// Address data
+ /// Add address info
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Contact information
+ ///
+ ///
+ ///
+ [Create(@"contact/{contactid:int}/addressdata")]
+ public ContactInfoDto CreateContactInfoAddress([FromRoute] int contactid, Address address)
+ {
+ if (contactid <= 0) throw new ArgumentException("Invalid value", "contactid");
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+
+ if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+
+ if (address == null) throw new ArgumentException("Value cannot be null", "address");
+
+ if (!Enum.IsDefined(typeof(AddressCategory), address.Category)) throw new ArgumentException("Value does not fall within the expected range.", "address.Category");
+
+ address.CategoryName = ((AddressCategory)address.Category).ToLocalizedString();
+
+ var settings = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ };
+
+ var contactInfo = new ContactInfo
+ {
+ InfoType = ContactInfoType.Address,
+ ContactID = contactid,
+ IsPrimary = address.IsPrimary,
+ Category = address.Category,
+ Data = JsonSerializer.Serialize(address, settings)
+ };
+
+ contactInfo.ID = _daoFactory.GetContactInfoDao().Save(contactInfo);
+
+ var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo;
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ return _mapper.Map(contactInfo);
+ }
+
+ ///
+ /// Creates contact information (add new information to the old list) with the parameters specified in the request for the contact with the selected ID
+ ///
+ ///Group contact info
+ /// Contact ID
+ /// Contact information
+ ///
+ ///
+ ///
+ /// Contacts
+ ///
+ ///
+ /// Contact information
+ ///
+ /// false
+ [Create(@"contact/{contactid:int}/batch")]
+ public IEnumerable CreateBatchContactInfo([FromRoute] int contactid, [FromForm] IEnumerable items)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+
+ var itemsList = items != null ? items.ToList() : new List();
+ var contactInfoList = itemsList.Select(FromContactInfoDto).ToList();
+
+ foreach (var contactInfo in contactInfoList)
+ {
+ if (contactInfo.InfoType == ContactInfoType.Address)
+ {
+ Address res;
+ if (!Address.TryParse(contactInfo, out res))
+ throw new ArgumentException();
+ }
+ contactInfo.ContactID = contactid;
+ }
+
+ var ids = _daoFactory.GetContactInfoDao().SaveList(contactInfoList, contact);
+
+ for (var index = 0; index < itemsList.Count; index++)
+ {
+ var infoDto = itemsList[index];
+ infoDto.Id = ids[index];
+ }
+ return itemsList;
+ }
+
+ ///
+ /// Updates the information with the parameters specified in the request for the contact with the selected ID
+ ///
+ ///Contact information record ID
+ ///Contact ID
+ ///Contact information type
+ ///Data
+ ///Contact importance: primary or not
+ ///Contact information category
+ ///Update contact info
+ ///Contacts
+ ///
+ ///
+ /// Contact information
+ ///
+ [Update(@"contact/{contactid:int}/data/{id:int}")]
+ public ContactInfoDto UpdateContactInfo(int id, int contactid, ContactInfoType? infoType, string data, bool? isPrimary, string category)
+ {
+ if (id <= 0 || string.IsNullOrEmpty(data) || contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+
+ var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id);
+
+ if (infoType != null)
+ {
+ var categoryType = ContactInfo.GetCategory(infoType.Value);
+
+ if (!string.IsNullOrEmpty(category) && Enum.IsDefined(categoryType, category))
+ {
+ contactInfo.Category = (int)Enum.Parse(categoryType, category);
+ }
+
+ contactInfo.InfoType = infoType.Value;
+ }
+
+ contactInfo.ContactID = contactid;
+
+ if (isPrimary != null)
+ {
+ contactInfo.IsPrimary = isPrimary.Value;
+ }
+
+ contactInfo.Data = data;
+
+ if (contactInfo.InfoType == ContactInfoType.Address)
+ {
+ Address res;
+ if (!Address.TryParse(contactInfo, out res))
+ throw new ArgumentException();
+ }
+
+ _daoFactory.GetContactInfoDao().Update(contactInfo);
+
+ var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo;
+
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ var contactInfoDto = _mapper.Map(contactInfo);
+
+ return contactInfoDto;
+ }
+
+ ///
+ /// Updates the address information with the parameters specified in the request for the contact with the selected ID
+ ///
+ /// Contact information record ID
+ /// Contact ID
+ /// Address data
+ /// Update address info
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Contact information
+ ///
+ [Update(@"contact/{contactid:int}/addressdata/{id:int}")]
+ public ContactInfoDto UpdateContactInfoAddress(int id, int contactid, Address address)
+ {
+ if (id <= 0) throw new ArgumentException("Invalid value", "id");
+
+ var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id);
+
+ if (contactInfo == null || contactInfo.InfoType != ContactInfoType.Address) throw new ItemNotFoundException();
+
+ if (contactid <= 0) throw new ArgumentException("Invalid value", "contactid");
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+
+ if (contact == null || !_crmSecurity.CanEdit(contact) || contactInfo.ContactID != contactid) throw new ItemNotFoundException();
+
+ if (address == null) throw new ArgumentException("Value cannot be null", "address");
+
+ if (!Enum.IsDefined(typeof(AddressCategory), address.Category)) throw new ArgumentException("Value does not fall within the expected range.", "address.Category");
+
+ address.CategoryName = ((AddressCategory)address.Category).ToLocalizedString();
+
+ var settings = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ };
+
+ contactInfo.IsPrimary = address.IsPrimary;
+ contactInfo.Category = address.Category;
+ contactInfo.Data = JsonSerializer.Serialize(address, settings);
+
+ _daoFactory.GetContactInfoDao().Update(contactInfo);
+
+ var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo;
+
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ return _mapper.Map(contactInfo);
+ }
+
+ ///
+ /// Updates contact information (delete old information and add new list) with the parameters specified in the request for the contact with the selected ID
+ ///
+ ///Group contact info update
+ ///Contact ID
+ ///Contact information
+ ///
+ ///Contacts
+ ///
+ ///
+ /// Contact information
+ ///
+ /// false
+ [Update(@"contact/{contactid:int}/batch")]
+ public IEnumerable UpdateBatchContactInfo(int contactid, IEnumerable items)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+
+ var itemsList = items != null ? items.ToList() : new List();
+ var contactInfoList = itemsList.Select(FromContactInfoDto).ToList();
+
+ foreach (var contactInfo in contactInfoList)
+ {
+ if (contactInfo.InfoType == ContactInfoType.Address)
+ {
+ Address res;
+ if (!Address.TryParse(contactInfo, out res))
+ throw new ArgumentException();
+ }
+ contactInfo.ContactID = contactid;
+ }
+
+ _daoFactory.GetContactInfoDao().DeleteByContact(contactid);
+ var ids = _daoFactory.GetContactInfoDao().SaveList(contactInfoList, contact);
+
+ for (var index = 0; index < itemsList.Count; index++)
+ {
+ var infoDto = itemsList[index];
+ infoDto.Id = ids[index];
+ }
+ return itemsList;
+ }
+
+ ///
+ /// Returns the detailed information for the contact with the selected ID by the information type specified in the request
+ ///
+ /// Contact ID
+ /// Contact information type
+ /// Get contact information by type
+ /// Contacts
+ ///
+ /// Contact information
+ ///
+ [Read(@"contact/{contactid:int}/data/{infoType}")]
+ public IEnumerable GetContactInfo(int contactid, ContactInfoType infoType)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ return _daoFactory.GetContactInfoDao().GetListData(contactid, infoType);
+ }
+
+
+ ///
+ /// Deletes the contact information for the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// Contact information record ID
+ /// Delete contact info
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Contact information
+ ///
+ [Delete(@"contact/{contactid:int}/data/{id:int}")]
+ public ContactInfoDto DeleteContactInfo(int contactid, int id)
+ {
+ if (id <= 0 || contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException();
+
+ var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id);
+ if (contactInfo == null) throw new ItemNotFoundException();
+
+ var wrapper = _mapper.Map(contactInfo);
+
+ _daoFactory.GetContactInfoDao().Delete(id);
+
+ var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo;
+
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ return wrapper;
+ }
+
+ private static ContactInfo FromContactInfoDto(ContactInfoDto contactInfoDto)
+ {
+ return new ContactInfo
+ {
+ ID = contactInfoDto.Id,
+ Category = contactInfoDto.Category,
+ Data = contactInfoDto.Data,
+ InfoType = contactInfoDto.InfoType,
+ IsPrimary = contactInfoDto.IsPrimary
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/Server/Api/ContactsController.cs b/products/ASC.CRM/Server/Api/ContactsController.cs
new file mode 100644
index 00000000000..56a722aca99
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/ContactsController.cs
@@ -0,0 +1,2146 @@
+/*
+ *
+ * (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.Api.Collections;
+using ASC.Api.Core;
+using ASC.Api.CRM;
+using ASC.Common.Threading.Progress;
+using ASC.Common.Web;
+using ASC.Core;
+using ASC.Core.Users;
+using ASC.CRM.ApiModels;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Core.Entities;
+using ASC.CRM.Core.Enums;
+using ASC.CRM.Resources;
+using ASC.MessagingSystem;
+using ASC.Web.Api.Models;
+using ASC.Web.Api.Routing;
+using ASC.Web.CRM.Classes;
+using ASC.Web.CRM.Services.NotifyService;
+using ASC.Web.Studio.Core;
+
+using Autofac;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+using Contact = ASC.CRM.Core.Entities.Contact;
+
+namespace ASC.CRM.Api
+{
+ public class ContactsController : BaseApiController
+ {
+ private readonly MailSender _mailSender;
+ private readonly FileSizeComment _fileSizeComment;
+ private readonly ContactPhotoManager _contactPhotoManager;
+ private readonly EmployeeWraperHelper _employeeWraperHelper;
+ private readonly UserFormatter _userFormatter;
+ private readonly SetupInfo _setupInfo;
+ private readonly SecurityContext _securityContext;
+ private readonly NotifyClient _notifyClient;
+ private readonly ApiContext _apiContext;
+ private readonly MessageService _messageService;
+ private readonly MessageTarget _messageTarget;
+
+ public ContactsController(CrmSecurity crmSecurity,
+ DaoFactory daoFactory,
+ ApiContext apiContext,
+ MessageTarget messageTarget,
+ MessageService messageService,
+ NotifyClient notifyClient,
+ SecurityContext securityContext,
+ SetupInfo setupInfo,
+ UserFormatter userFormatter,
+ EmployeeWraperHelper employeeWraperHelper,
+ ContactPhotoManager contactPhotoManager,
+ FileSizeComment fileSizeComment,
+ MailSender mailSender,
+ IMapper mapper)
+ : base(daoFactory, crmSecurity, mapper)
+ {
+ _apiContext = apiContext;
+ _messageTarget = messageTarget;
+ _messageService = messageService;
+ _notifyClient = notifyClient;
+ _securityContext = securityContext;
+ _setupInfo = setupInfo;
+ _userFormatter = userFormatter;
+ _employeeWraperHelper = employeeWraperHelper;
+ _contactPhotoManager = contactPhotoManager;
+ _fileSizeComment = fileSizeComment;
+ _mailSender = mailSender;
+ _mapper = mapper;
+ }
+
+
+ ///
+ /// Returns the detailed information about the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// Contact
+ /// Get contact by ID
+ /// Contacts
+ ///
+ ///
+ [Read(@"contact/{contactid:int}")]
+ public ContactDto GetContactByID(int contactid)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ return _mapper.Map(contact);
+ }
+
+ public IEnumerable GetContactsByID(IEnumerable contactid)
+ {
+ var contacts = _daoFactory.GetContactDao().GetContacts(contactid.ToArray()).Where(r => r != null && _crmSecurity.CanAccessTo(r)).ToList();
+
+ return _mapper.Map, List>(contacts);
+ }
+
+ ///
+ /// Returns the contact list for the project with the ID specified in the request
+ ///
+ ///
+ /// Get contacts by project ID
+ ///
+ /// Project ID
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ ///
+ [Read(@"contact/project/{projectid:int}")]
+ public IEnumerable GetContactsByProjectID(int projectid)
+ {
+ if (projectid <= 0) throw new ArgumentException();
+
+ var contacts = _daoFactory.GetContactDao().GetContactsByProjectID(projectid);
+
+ return _mapper.Map, List>(contacts.ToList());
+
+ }
+
+ /////
+ ///// Links the selected contact to the project with the ID specified in the request
+ /////
+ ///// Contact ID
+ ///// Project ID
+ ///// Contacts
+ ///// Link contact with project
+ /////
+ /////
+ ///// Contact Info
+ //[Create(@"contact/{contactid:int}/project/{projectid:int}")]
+ //public ContactDto SetRelativeContactToProject(int contactid, int projectid)
+ //{
+ // if (contactid <= 0 || projectid <= 0) throw new ArgumentException();
+
+ // var contact = DaoFactory.GetContactDao().GetByID(contactid);
+ // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid);
+ // if (project == null) throw new ItemNotFoundException();
+
+ // using (var scope = DIHelper.Resolve())
+ // {
+ // if (!scope.Resolve().CanLinkContact(project)) throw CRMSecurity.CreateSecurityException();
+ // }
+
+ // DaoFactory.GetContactDao().SetRelativeContactProject(new List { contactid }, projectid);
+
+ // var messageAction = contact is Company ? MessageAction.ProjectLinkedCompany : MessageAction.ProjectLinkedPerson;
+ // MessageService.Send(messageAction, MessageTarget.Create(contact.ID), project.Title, contact.GetTitle());
+
+ // return ToContactDto(contact);
+ //}
+
+ /////
+ ///// Links the selected contacts to the project with the ID specified in the request
+ /////
+ ///// Contact IDs array
+ ///// Project ID
+ ///// Contacts
+ ///// Link contact list with project
+ /////
+ /////
+ /////
+ ///// Contact list
+ /////
+ //[Create(@"contact/project/{projectid:int}")]
+ //public IEnumerable SetRelativeContactListToProject(IEnumerable contactid, int projectid)
+ //{
+ // if (contactid == null) throw new ArgumentException();
+
+ // var contactIds = contactid.ToList();
+
+ // if (!contactIds.Any() || projectid <= 0) throw new ArgumentException();
+
+ // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid);
+ // if (project == null) throw new ItemNotFoundException();
+
+ // using (var scope = DIHelper.Resolve())
+ // {
+ // if (!scope.Resolve().CanLinkContact(project))
+ // throw CRMSecurity.CreateSecurityException();
+ // }
+
+
+ // var contacts = DaoFactory.GetContactDao().GetContacts(contactIds.ToArray()).Where(CRMSecurity.CanAccessTo).ToList();
+ // contactIds = contacts.Select(c => c.ID).ToList();
+
+ // DaoFactory.GetContactDao().SetRelativeContactProject(contactIds, projectid);
+
+ // MessageService.Send(MessageAction.ProjectLinkedContacts, MessageTarget.Create(contactIds), project.Title, contacts.Select(x => x.GetTitle()));
+
+ // return contacts.ConvertAll(ToContactDto);
+ //}
+
+ /////
+ ///// Removes the link with the selected project from the contact with the ID specified in the request
+ /////
+ ///// Contact ID
+ ///// Project ID
+ ///// Contacts
+ ///// Remove contact from project
+ /////
+ ///// Contact info
+ /////
+ //[Delete(@"contact/{contactid:int}/project/{projectid:int}")]
+ //public ContactBaseDto RemoveRelativeContactToProject(int contactid, int projectid)
+ //{
+ // if (contactid <= 0 || projectid <= 0) throw new ArgumentException();
+
+ // var contact = DaoFactory.GetContactDao().GetByID(contactid);
+ // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid);
+
+ // using (var scope = DIHelper.Resolve())
+ // {
+ // if (project == null || !scope.Resolve().CanLinkContact(project)) throw new ItemNotFoundException();
+ // }
+
+ // DaoFactory.GetContactDao().RemoveRelativeContactProject(contactid, projectid);
+
+ // var action = contact is Company ? MessageAction.ProjectUnlinkedCompany : MessageAction.ProjectUnlinkedPerson;
+ // MessageService.Send(action, MessageTarget.Create(contact.ID), project.Title, contact.GetTitle());
+
+ // return ToContactBaseDto(contact);
+ //}
+
+ ///
+ /// Adds the selected opportunity to the contact with the ID specified in the request. The same as AddMemberToDeal
+ ///
+ /// Opportunity ID
+ /// Contact ID
+ /// Add contact opportunity
+ /// Contacts
+ ///
+ ///
+ /// Opportunity
+ ///
+ [Create(@"contact/{contactid:int}/opportunity/{opportunityid:int}")]
+ public OpportunityDto AddDealToContact([FromRoute] int contactid, [FromRoute] int opportunityid)
+ {
+ if ((opportunityid <= 0) || (contactid <= 0)) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid);
+ if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException();
+
+ _daoFactory.GetDealDao().AddMember(opportunityid, contactid);
+
+ var messageAction = contact is Company ? MessageAction.OpportunityLinkedCompany : MessageAction.OpportunityLinkedPerson;
+
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), opportunity.Title, contact.GetTitle());
+
+ return _mapper.Map(opportunity);
+ }
+
+ ///
+ /// Deletes the selected opportunity from the contact with the ID specified in the request
+ ///
+ /// Opportunity ID
+ /// Contact ID
+ /// Delete contact opportunity
+ /// Contacts
+ ///
+ ///
+ /// Opportunity
+ ///
+ [Delete(@"contact/{contactid:int}/opportunity/{opportunityid:int}")]
+ public OpportunityDto DeleteDealFromContact(int contactid, int opportunityid)
+ {
+ if ((opportunityid <= 0) || (contactid <= 0)) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid);
+ if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException();
+
+ _daoFactory.GetDealDao().RemoveMember(opportunityid, contactid);
+
+ return _mapper.Map(opportunity);
+ }
+
+ ///
+ /// Returns the list of all contacts in the CRM module matching the parameters specified in the request
+ ///
+ /// Tag
+ /// Contact stage ID (warmth)
+ /// Contact type ID
+ ///
+ /// Start date
+ /// End date
+ /// Responsible ID
+ /// Responsible ID
+ /// Get contact list
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ [Read(@"contact/filter")]
+ public IEnumerable GetContacts(
+ [FromQuery] IEnumerable tags,
+ [FromQuery] int? contactStage,
+ [FromQuery] int? contactType,
+ [FromQuery] ContactListViewType contactListView,
+ [FromQuery] Guid? responsibleid,
+ [FromQuery] bool? isShared,
+ [FromQuery] ApiDateTime fromDate,
+ [FromQuery] ApiDateTime toDate)
+ {
+ IEnumerable result;
+ OrderBy contactsOrderBy;
+ ContactSortedByType sortBy;
+
+ var searchString = _apiContext.FilterValue;
+
+ if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy))
+ {
+ contactsOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending);
+ }
+ else if (String.IsNullOrEmpty(_apiContext.SortBy))
+ {
+ contactsOrderBy = new OrderBy(ContactSortedByType.Created, false);
+ }
+ else
+ {
+ contactsOrderBy = null;
+ }
+
+
+ var fromIndex = (int)_apiContext.StartIndex;
+ var count = (int)_apiContext.Count;
+ var contactStageInt = contactStage.HasValue ? contactStage.Value : -1;
+ var contactTypeInt = contactType.HasValue ? contactType.Value : -1;
+
+ if (contactsOrderBy != null)
+ {
+
+ var contacts = _daoFactory.GetContactDao().GetContacts(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ fromIndex,
+ count,
+ contactsOrderBy,
+ responsibleid,
+ isShared);
+
+ result = _mapper.Map, List>(contacts);
+
+ _apiContext.SetDataPaginated();
+ _apiContext.SetDataFiltered();
+ _apiContext.SetDataSorted();
+ }
+ else
+ {
+ result = _mapper.Map, List>(_daoFactory.GetContactDao().GetContacts(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ 0,
+ 0,
+ null,
+ responsibleid,
+ isShared));
+ }
+
+ int totalCount;
+
+ if (result.Count() < count)
+ {
+ totalCount = fromIndex + result.Count();
+ }
+ else
+ {
+ totalCount = _daoFactory.GetContactDao().GetContactsCount(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ responsibleid,
+ isShared);
+ }
+
+ _apiContext.SetTotalCount(totalCount);
+
+ return result;
+ }
+
+ ///
+ /// Returns the list of the contacts for auto complete feature.
+ ///
+ /// String part of contact name, lastname or email.
+ /// Max result count
+ /// Search contact list
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ /// false
+ [Read(@"contact/simple/byEmail")]
+ public IEnumerable SearchContactsByEmail(string term, int maxCount)
+ {
+ var result = ToSimpleListContactDto(_daoFactory.GetContactDao().SearchContactsByEmail(
+ term,
+ maxCount));
+
+ return result;
+ }
+
+ ///
+ /// Returns the list of all contacts in the CRM module matching the parameters specified in the request
+ ///
+ /// Tag
+ /// Contact stage ID (warmth)
+ /// Contact type ID
+ ///
+ /// Responsible ID
+ /// Responsible ID
+ /// Start date
+ /// End date
+ /// Get contact list
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ /// false
+ [Read(@"contact/simple/filter")]
+ public IEnumerable GetSimpleContacts(
+ IEnumerable tags,
+ int? contactStage,
+ int? contactType,
+ ContactListViewType contactListView,
+ Guid? responsibleid,
+ bool? isShared,
+ ApiDateTime fromDate,
+ ApiDateTime toDate)
+ {
+ IEnumerable result;
+
+ OrderBy contactsOrderBy;
+
+ ContactSortedByType sortBy;
+
+ var searchString = _apiContext.FilterValue;
+ if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy))
+ {
+ contactsOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending);
+ }
+ else if (String.IsNullOrEmpty(_apiContext.SortBy))
+ {
+ contactsOrderBy = new OrderBy(ContactSortedByType.DisplayName, true);
+ }
+ else
+ {
+ contactsOrderBy = null;
+ }
+
+ var fromIndex = (int)_apiContext.StartIndex;
+ var count = (int)_apiContext.Count;
+ var contactStageInt = contactStage.HasValue ? contactStage.Value : -1;
+ var contactTypeInt = contactType.HasValue ? contactType.Value : -1;
+
+ if (contactsOrderBy != null)
+ {
+ result = ToSimpleListContactDto(_daoFactory.GetContactDao().GetContacts(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ fromIndex,
+ count,
+ contactsOrderBy,
+ responsibleid,
+ isShared));
+ _apiContext.SetDataPaginated();
+ _apiContext.SetDataFiltered();
+ _apiContext.SetDataSorted();
+ }
+ else
+ {
+ result = ToSimpleListContactDto(_daoFactory.GetContactDao().GetContacts(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ 0,
+ 0,
+ null,
+ responsibleid,
+ isShared));
+ }
+
+ int totalCount;
+
+ if (result.Count() < count)
+ {
+ totalCount = fromIndex + result.Count();
+ }
+ else
+ {
+ totalCount = _daoFactory.GetContactDao().GetContactsCount(
+ searchString,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ responsibleid,
+ isShared);
+ }
+
+ _apiContext.SetTotalCount(totalCount);
+
+ return result;
+ }
+
+ ///
+ /// Get the group of contacts with the IDs specified in the request
+ ///
+ /// Contact ID list
+ ///
+ ///
+ /// Get contact group
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ /// false
+ [Read(@"contact/mail")]
+ public IEnumerable GetContactsForMail(IEnumerable contactids)
+ {
+ if (contactids == null) throw new ArgumentException();
+
+ var contacts = _daoFactory.GetContactDao().GetContacts(contactids.ToArray());
+
+ var result = contacts.Select(x => _mapper.Map(x));
+ return result;
+ }
+
+ ///
+ /// Deletes the list of all contacts in the CRM module matching the parameters specified in the request
+ ///
+ /// Tag
+ /// Contact stage ID (warmth)
+ /// Contact type ID
+ ///
+ /// Start date
+ /// End date
+ ///
+ ///
+ /// Delete the list of all contacts
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ [Delete(@"contact/filter")]
+ public IEnumerable DeleteBatchContacts(
+ IEnumerable tags,
+ int? contactStage,
+ int? contactType,
+ ContactListViewType contactListView,
+ ApiDateTime fromDate,
+ ApiDateTime toDate)
+ {
+ int contactStageInt = contactStage.HasValue ? contactStage.Value : -1;
+ int contactTypeInt = contactType.HasValue ? contactType.Value : -1;
+
+
+ var contacts = _daoFactory.GetContactDao().GetContacts(
+ _apiContext.FilterValue,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate,
+ toDate,
+ 0,
+ 0,
+ null);
+
+ contacts = _daoFactory.GetContactDao().DeleteBatchContact(contacts);
+
+ _messageService.Send(MessageAction.ContactsDeleted, _messageTarget.Create(contacts.Select(c => c.ID)), contacts.Select(c => c.GetTitle()));
+
+ return contacts.Select(x => _mapper.Map(x));
+ }
+
+
+ ///
+ /// Returns the list of all the persons linked to the company with the ID specified in the request
+ ///
+ /// Company ID
+ ///
+ /// Get company linked persons list
+ /// Contacts
+ ///
+ /// Linked persons
+ ///
+ [Read(@"contact/company/{companyid:int}/person")]
+ public IEnumerable GetPeopleFromCompany(int companyid)
+ {
+ if (companyid <= 0) throw new ArgumentException();
+
+ var company = _daoFactory.GetContactDao().GetByID(companyid);
+ if (company == null || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException();
+
+ var contacts = _daoFactory.GetContactDao().GetMembers(companyid).Where(_crmSecurity.CanAccessTo).ToList();
+
+ return _mapper.Map, List>(contacts);
+ }
+
+ ///
+ /// Adds the selected person to the company with the ID specified in the request
+ ///
+ /// Company ID
+ /// Person ID
+ /// Add person to company
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Person
+ ///
+ [Create(@"contact/company/{companyid:int}/person")]
+ public PersonDto AddPeopleToCompany([FromRoute] int companyid, [FromForm] int personid)
+ {
+ if ((companyid <= 0) || (personid <= 0)) throw new ArgumentException();
+
+ var company = _daoFactory.GetContactDao().GetByID(companyid);
+ var person = _daoFactory.GetContactDao().GetByID(personid);
+
+ if (person == null || company == null || !_crmSecurity.CanAccessTo(person) || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException();
+
+ _daoFactory.GetContactDao().AddMember(personid, companyid);
+
+ _messageService.Send(MessageAction.CompanyLinkedPerson, _messageTarget.Create(new[] { company.ID, person.ID }), company.GetTitle(), person.GetTitle());
+
+ return (PersonDto)_mapper.Map(person);
+ }
+
+ ///
+ /// Deletes the selected person from the company with the ID specified in the request
+ ///
+ /// Company ID
+ /// Person ID
+ /// Delete person from company
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Person
+ ///
+ [Delete(@"contact/company/{companyid:int}/person")]
+ public PersonDto DeletePeopleFromCompany(int companyid, int personid)
+ {
+ if ((companyid <= 0) || (personid <= 0)) throw new ArgumentException();
+
+ var company = _daoFactory.GetContactDao().GetByID(companyid);
+ var person = _daoFactory.GetContactDao().GetByID(personid);
+ if (person == null || company == null || !_crmSecurity.CanAccessTo(person) || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException();
+
+ _daoFactory.GetContactDao().RemoveMember(personid);
+
+ _messageService.Send(MessageAction.CompanyUnlinkedPerson, _messageTarget.Create(new[] { company.ID, person.ID }), company.GetTitle(), person.GetTitle());
+
+ return (PersonDto)_mapper.Map(person);
+ }
+
+ ///
+ /// Creates the person with the parameters (first name, last name, description, etc.) specified in the request
+ ///
+ /// First name
+ /// Last name
+ /// Post
+ /// Company ID
+ /// Person description text
+ /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only
+ /// List of managers for the person
+ /// User field list
+ /// Contact photo (upload using multipart/form-data)
+ /// Create person
+ /// Contacts
+ /// Person
+ ///
+ [Create(@"contact/person")]
+ public PersonDto CreatePerson([FromForm] CreateOrUpdatePersonRequestDto intDto)
+ {
+ string firstName = intDto.FirstName;
+ string lastName = intDto.LastName;
+ string jobTitle = intDto.JobTitle;
+ int companyId = intDto.CompanyId;
+ string about = intDto.About;
+ ShareType shareType = intDto.ShareType;
+ IEnumerable managerList = intDto.ManagerList;
+ IEnumerable> customFieldList = intDto.CustomFieldList;
+ IEnumerable photo = intDto.Photos;
+
+ if (companyId > 0)
+ {
+ var company = _daoFactory.GetContactDao().GetByID(companyId);
+ if (company == null || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException();
+ }
+
+ var peopleInst = new Person
+ {
+ FirstName = firstName,
+ LastName = lastName,
+ JobTitle = jobTitle,
+ CompanyID = companyId,
+ About = about,
+ ShareType = shareType
+ };
+
+ peopleInst.ID = _daoFactory.GetContactDao().SaveContact(peopleInst);
+ peopleInst.CreateBy = _securityContext.CurrentAccount.ID;
+ peopleInst.CreateOn = DateTime.UtcNow;
+
+ var managerListLocal = managerList != null ? managerList.ToList() : new List();
+ if (managerListLocal.Any())
+ {
+ _crmSecurity.SetAccessTo(peopleInst, managerListLocal);
+ }
+
+ if (customFieldList != null)
+ {
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Person, peopleInst.ID, field.Key, field.Value);
+ }
+ }
+
+ var outDto = (PersonDto)_mapper.Map(peopleInst);
+
+ var photoList = photo != null ? photo.ToList() : new List();
+
+ if (photoList.Any())
+ {
+ outDto.SmallFotoUrl = ChangeContactPhoto(peopleInst.ID, photoList);
+ }
+
+ _messageService.Send(MessageAction.PersonCreated, _messageTarget.Create(peopleInst.ID), peopleInst.GetTitle());
+
+ return outDto;
+ }
+
+ ///
+ /// Changes the photo for the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// Contact photo (upload using multipart/form-data)
+ /// Change contact photo
+ /// Contacts
+ ///
+ ///
+ /// Path to contact photo
+ ///
+ [Update(@"contact/{contactid:int}/changephoto")]
+ public string ChangeContactPhoto(int contactid, IEnumerable photo)
+ {
+ if (contactid <= 0)
+ throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+
+ if (contact == null || !_crmSecurity.CanAccessTo(contact))
+ throw new ItemNotFoundException();
+
+ var firstPhoto = photo != null ? photo.FirstOrDefault() : null;
+
+ if (firstPhoto == null)
+ throw new ArgumentException();
+
+ var fileStream = firstPhoto.OpenReadStream();
+
+ if (firstPhoto.Length == 0 ||
+ !firstPhoto.ContentType.StartsWith("image/") ||
+ !fileStream.CanRead)
+ throw new InvalidOperationException(CRMErrorsResource.InvalidFile);
+
+ if (_setupInfo.MaxImageUploadSize > 0 &&
+ _setupInfo.MaxImageUploadSize < firstPhoto.Length)
+ throw new Exception(_fileSizeComment.GetFileImageSizeNote(CRMCommonResource.ErrorMessage_UploadFileSize, false));
+
+ return _contactPhotoManager.UploadPhoto(fileStream, contactid, false).Url;
+ }
+
+ ///
+ /// Changes the photo for the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// contact photo url
+ /// Change contact photo
+ /// Contacts
+ ///
+ ///
+ /// Path to contact photo
+ ///
+ [Update(@"contact/{contactid:int}/changephotobyurl")]
+ public string ChangeContactPhoto(int contactid, string photourl)
+ {
+ if (contactid <= 0 || string.IsNullOrEmpty(photourl)) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ return _contactPhotoManager.UploadPhoto(photourl, contactid, false).Url;
+ }
+
+ ///
+ /// Merge two selected contacts
+ ///
+ /// the first contact ID for merge
+ /// the second contact ID for merge
+ /// Merge contacts
+ /// Contacts
+ ///
+ ///
+ ///
+ ///
+ /// Contact
+ ///
+ [Update(@"contact/merge")]
+ public ContactDto MergeContacts(int fromcontactid, int tocontactid)
+ {
+ if (fromcontactid <= 0 || tocontactid <= 0) throw new ArgumentException();
+
+ var fromContact = _daoFactory.GetContactDao().GetByID(fromcontactid);
+ var toContact = _daoFactory.GetContactDao().GetByID(tocontactid);
+
+ if (fromContact == null || toContact == null) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanEdit(fromContact) || !_crmSecurity.CanEdit(toContact)) throw _crmSecurity.CreateSecurityException();
+
+ _daoFactory.GetContactDao().MergeDublicate(fromcontactid, tocontactid);
+ var resultContact = _daoFactory.GetContactDao().GetByID(tocontactid);
+
+ var messageAction = resultContact is Person ? MessageAction.PersonsMerged : MessageAction.CompaniesMerged;
+ _messageService.Send(messageAction, _messageTarget.Create(new[] { fromContact.ID, toContact.ID }), fromContact.GetTitle(), toContact.GetTitle());
+
+ return _mapper.Map(resultContact);
+ }
+
+ ///
+ /// Updates the selected person with the parameters (first name, last name, description, etc.) specified in the request
+ ///
+ /// Person ID
+ /// First name
+ /// Last name
+ /// Post
+ /// Company ID
+ /// Person description text
+ /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only
+ /// List of persons managers
+ /// User field list
+ /// Contact photo (upload using multipart/form-data)
+ /// Update person
+ /// Contacts
+ /// Person
+ ///
+ ///
+ [Update(@"contact/person/{personid:int}")]
+ public PersonDto UpdatePerson([FromQuery] int personid, [FromForm] CreateOrUpdatePersonRequestDto inDto)
+ {
+ string firstName = inDto.FirstName;
+ string lastName = inDto.LastName;
+ string jobTitle = inDto.JobTitle;
+ int companyId = inDto.CompanyId;
+ string about = inDto.About;
+ ShareType shareType = inDto.ShareType;
+ IEnumerable managerList = inDto.ManagerList;
+ IEnumerable> customFieldList = inDto.CustomFieldList;
+ IEnumerable photo = inDto.Photos;
+
+ if (personid <= 0 || string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName)) throw new ArgumentException();
+
+ var peopleInst = new Person
+ {
+ ID = personid,
+ FirstName = firstName,
+ LastName = lastName,
+ JobTitle = jobTitle,
+ CompanyID = companyId,
+ About = about,
+ ShareType = shareType
+ };
+
+ _daoFactory.GetContactDao().UpdateContact(peopleInst);
+
+ peopleInst = (Person)_daoFactory.GetContactDao().GetByID(peopleInst.ID);
+
+ var managerListLocal = managerList != null ? managerList.ToList() : new List();
+ if (managerListLocal.Any())
+ {
+ _crmSecurity.SetAccessTo(peopleInst, managerListLocal);
+ }
+
+ if (customFieldList != null)
+ {
+ var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Person).Select(fd => fd.ID).ToList();
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Person, peopleInst.ID, field.Key, field.Value);
+ }
+ }
+
+ var outDto = (PersonDto)_mapper.Map(peopleInst);
+
+ var photoList = photo != null ? photo.ToList() : new List();
+
+ if (photoList.Any())
+ {
+ outDto.SmallFotoUrl = ChangeContactPhoto(peopleInst.ID, photoList);
+ }
+
+ _messageService.Send(MessageAction.PersonUpdated, _messageTarget.Create(peopleInst.ID), peopleInst.GetTitle());
+
+ return outDto;
+ }
+
+ ///
+ /// Creates the company with the parameters specified in the request
+ ///
+ /// Company name
+ /// Company description text
+ /// Linked person list
+ /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only
+ /// List of managers for the company
+ /// User field list
+ /// Contact photo (upload using multipart/form-data)
+ /// Create company
+ /// Contacts
+ /// Company
+ ///
+ [Create(@"contact/company")]
+ public CompanyDto CreateCompany([FromForm] CreateOrUpdateCompanyRequestDto inDto)
+ {
+ var personList = inDto.PersonList;
+ string companyName = inDto.CompanyName;
+ string about = inDto.About;
+ ShareType shareType = inDto.ShareType;
+ IEnumerable managerList = inDto.ManagerList;
+ IEnumerable> customFieldList = inDto.CustomFieldList;
+ IEnumerable photo = inDto.Photos;
+
+ var companyInst = new Company
+ {
+ CompanyName = companyName,
+ About = about,
+ ShareType = shareType
+ };
+
+ companyInst.ID = _daoFactory.GetContactDao().SaveContact(companyInst);
+ companyInst.CreateBy = _securityContext.CurrentAccount.ID;
+ companyInst.CreateOn = DateTime.UtcNow;
+
+ if (personList != null)
+ {
+ foreach (var personID in personList)
+ {
+ var person = _daoFactory.GetContactDao().GetByID(personID);
+ if (person == null || !_crmSecurity.CanAccessTo(person)) continue;
+
+ AddPeopleToCompany(companyInst.ID, personID);
+ }
+ }
+
+ var managerListLocal = managerList != null ? managerList.ToList() : new List();
+ if (managerListLocal.Any())
+ {
+ _crmSecurity.SetAccessTo(companyInst, managerListLocal);
+ }
+
+ if (customFieldList != null)
+ {
+ var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Company).Select(fd => fd.ID).ToList();
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Company, companyInst.ID, field.Key, field.Value);
+ }
+ }
+
+ var wrapper = (CompanyDto)_mapper.Map(companyInst);
+
+ var photoList = photo != null ? photo.ToList() : new List();
+ if (photoList.Any())
+ {
+ wrapper.SmallFotoUrl = ChangeContactPhoto(companyInst.ID, photoList);
+ }
+
+ _messageService.Send(MessageAction.CompanyCreated, _messageTarget.Create(companyInst.ID), companyInst.GetTitle());
+
+ return wrapper;
+ }
+
+ ///
+ /// Quickly creates the list of companies
+ ///
+ ///
+ /// Quick company list creation
+ ///
+ /// Company name
+ /// Contacts
+ /// Contact list
+ ///
+ [Create(@"contact/company/quick")]
+ public IEnumerable CreateCompany([FromForm] IEnumerable companyName)
+ {
+ if (companyName == null) throw new ArgumentException();
+
+ var contacts = new List();
+ var recordIndex = 0;
+
+ foreach (var item in companyName)
+ {
+ if (string.IsNullOrEmpty(item)) continue;
+
+ contacts.Add(new Company
+ {
+ ID = recordIndex++,
+ CompanyName = item,
+ ShareType = ShareType.None
+ });
+ }
+
+ if (contacts.Count == 0) return null;
+
+ _daoFactory.GetContactDao().SaveContactList(contacts);
+
+ var selectedManagers = new List { _securityContext.CurrentAccount.ID };
+
+ foreach (var ct in contacts)
+ {
+ _crmSecurity.SetAccessTo(ct, selectedManagers);
+ }
+
+ return contacts.ConvertAll(x => _mapper.Map(x));
+ }
+
+ ///
+ /// Quickly creates the list of persons with the first and last names specified in the request
+ ///
+ ///
+ /// Quick person list creation
+ ///
+ /// Pairs: user first name, user last name
+ ///
+ ///
+ ///
+ /// Contacts
+ /// Contact list
+ ///
+ [Create(@"contact/person/quick")]
+ public IEnumerable CreatePerson([FromForm] IEnumerable> data)
+ {
+ if (data == null) return null;
+
+ var contacts = new List();
+ var recordIndex = 0;
+
+ foreach (var item in data)
+ {
+ if (string.IsNullOrEmpty(item.Key) || string.IsNullOrEmpty(item.Value)) continue;
+
+ contacts.Add(new Person
+ {
+ ID = recordIndex++,
+ FirstName = item.Key,
+ LastName = item.Value,
+ ShareType = ShareType.None
+ });
+ }
+
+ if (contacts.Count == 0) return null;
+
+ _daoFactory.GetContactDao().SaveContactList(contacts);
+
+ var selectedManagers = new List { _securityContext.CurrentAccount.ID };
+
+ foreach (var ct in contacts)
+ {
+ _crmSecurity.SetAccessTo(ct, selectedManagers);
+ }
+
+ _messageService.Send(MessageAction.PersonsCreated, _messageTarget.Create(contacts.Select(x => x.ID)), contacts.Select(x => x.GetTitle()));
+
+ return contacts.ConvertAll(x => _mapper.Map(x));
+ }
+
+ ///
+ /// Updates the selected company with the parameters specified in the request
+ ///
+ /// Company ID
+ /// Company name
+ /// Company description text
+ /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only
+ /// List of company managers
+ /// User field list
+ /// Update company
+ /// Contacts
+ ///
+ ///
+ /// Company
+ ///
+ [Update(@"contact/company/{companyid:int}")]
+ public CompanyDto UpdateCompany(
+ int companyid,
+ [FromForm] CreateOrUpdateCompanyRequestDto intDto)
+ {
+ string companyName = intDto.CompanyName;
+ string about = intDto.About;
+ ShareType shareType = intDto.ShareType;
+ IEnumerable managerList = intDto.ManagerList;
+ IEnumerable> customFieldList = intDto.CustomFieldList;
+ IEnumerable photo = intDto.Photos;
+
+ var companyInst = new Company
+ {
+ ID = companyid,
+ CompanyName = companyName,
+ About = about,
+ ShareType = shareType
+ };
+
+ _daoFactory.GetContactDao().UpdateContact(companyInst);
+
+ companyInst = (Company)_daoFactory.GetContactDao().GetByID(companyInst.ID);
+
+ var managerListLocal = managerList != null ? managerList.ToList() : new List();
+ if (managerListLocal.Any())
+ {
+ _crmSecurity.SetAccessTo(companyInst, managerListLocal);
+ }
+
+ if (customFieldList != null)
+ {
+ var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Company).Select(fd => fd.ID).ToList();
+ foreach (var field in customFieldList)
+ {
+ if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue;
+ _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Company, companyInst.ID, field.Key, field.Value);
+ }
+ }
+
+ _messageService.Send(MessageAction.CompanyUpdated, _messageTarget.Create(companyInst.ID), companyInst.GetTitle());
+
+ return (CompanyDto)_mapper.Map(companyInst);
+ }
+
+ ///
+ /// Updates the selected contact status
+ ///
+ /// Contact ID
+ /// Contact status ID
+ /// Update status in contact by id
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Company
+ ///
+ [Update(@"contact/{contactid:int}/status")]
+ public ContactDto UpdateContactStatus(int contactid, int contactStatusid)
+ {
+ if (contactid <= 0 || contactStatusid < 0) throw new ArgumentException();
+
+ var dao = _daoFactory.GetContactDao();
+
+ if (contactStatusid > 0)
+ {
+ var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid);
+ if (curListItem == null) throw new ItemNotFoundException();
+ }
+
+ var companyInst = dao.GetByID(contactid);
+ if (companyInst == null || !_crmSecurity.CanAccessTo(companyInst)) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanEdit(companyInst)) throw _crmSecurity.CreateSecurityException();
+
+ dao.UpdateContactStatus(new List { companyInst.ID }, contactStatusid);
+ companyInst.StatusID = contactStatusid;
+
+ var messageAction = companyInst is Company ? MessageAction.CompanyUpdatedTemperatureLevel : MessageAction.PersonUpdatedTemperatureLevel;
+ _messageService.Send(messageAction, _messageTarget.Create(companyInst.ID), companyInst.GetTitle());
+
+ return _mapper.Map(companyInst);
+ }
+
+ ///
+ /// Updates status of the selected company and all its participants
+ ///
+ /// Company ID
+ /// Contact status ID
+ /// Update company and participants status
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Company
+ ///
+ [Update(@"contact/company/{companyid:int}/status")]
+ public ContactDto UpdateCompanyAndParticipantsStatus(int companyid, int contactStatusid)
+ {
+ if (companyid <= 0 || contactStatusid < 0) throw new ArgumentException();
+
+ var dao = _daoFactory.GetContactDao();
+
+ if (contactStatusid > 0)
+ {
+ var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid);
+ if (curListItem == null) throw new ItemNotFoundException();
+ }
+
+ var companyInst = dao.GetByID(companyid);
+ if (companyInst == null || !_crmSecurity.CanAccessTo(companyInst)) throw new ItemNotFoundException();
+
+ if (companyInst is Person) throw new Exception(CRMErrorsResource.ContactIsNotCompany);
+
+ var forUpdateStatus = new List();
+ forUpdateStatus.Add(companyInst.ID);
+
+ var members = dao.GetMembersIDsAndShareType(companyInst.ID);
+ foreach (var m in members)
+ {
+ if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0))
+ {
+ forUpdateStatus.Add(m.Key);
+ }
+ }
+
+ dao.UpdateContactStatus(forUpdateStatus, contactStatusid);
+
+ _messageService.Send(MessageAction.CompanyUpdatedTemperatureLevel, _messageTarget.Create(companyInst.ID), companyInst.GetTitle());
+ _messageService.Send(MessageAction.CompanyUpdatedPersonsTemperatureLevel, _messageTarget.Create(companyInst.ID), companyInst.GetTitle());
+
+ return _mapper.Map(companyInst);
+ }
+
+ ///
+ /// Updates status of the selected person, related company and all its participants
+ ///
+ /// Person ID
+ /// Contact status ID
+ /// Update person, related company and participants status
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Person
+ ///
+ [Update(@"contact/person/{personid:int}/status")]
+ public ContactDto UpdatePersonAndItsCompanyStatus(int personid, int contactStatusid)
+ {
+ if (personid <= 0 || contactStatusid < 0) throw new ArgumentException();
+
+ if (contactStatusid > 0)
+ {
+ var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid);
+ if (curListItem == null) throw new ItemNotFoundException();
+ }
+
+ var dao = _daoFactory.GetContactDao();
+
+ var personInst = dao.GetByID(personid);
+ if (personInst == null || !_crmSecurity.CanAccessTo(personInst)) throw new ItemNotFoundException();
+
+ if (personInst is Company) throw new Exception(CRMErrorsResource.ContactIsNotPerson);
+
+ var forUpdateStatus = new List();
+
+ var companyID = ((Person)personInst).CompanyID;
+ if (companyID != 0)
+ {
+ var companyInst = dao.GetByID(companyID);
+ if (companyInst == null) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanAccessTo(companyInst))
+ {
+ forUpdateStatus.Add(personInst.ID);
+ dao.UpdateContactStatus(forUpdateStatus, contactStatusid);
+ }
+ else
+ {
+ forUpdateStatus.Add(companyInst.ID);
+
+ var members = dao.GetMembersIDsAndShareType(companyInst.ID);
+ foreach (var m in members)
+ {
+ if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0))
+ {
+ forUpdateStatus.Add(m.Key);
+ }
+ }
+ dao.UpdateContactStatus(forUpdateStatus, contactStatusid);
+ }
+ }
+ else
+ {
+ forUpdateStatus.Add(personInst.ID);
+ dao.UpdateContactStatus(forUpdateStatus, contactStatusid);
+ }
+
+ _messageService.Send(MessageAction.PersonUpdatedTemperatureLevel, _messageTarget.Create(personInst.ID), personInst.GetTitle());
+ _messageService.Send(MessageAction.PersonUpdatedCompanyTemperatureLevel, _messageTarget.Create(personInst.ID), personInst.GetTitle());
+
+ personInst = dao.GetByID(personInst.ID);
+ return _mapper.Map(personInst);
+ }
+
+ ///
+ /// Get access rights to the contact with the ID specified in the request
+ ///
+ /// Get contact access rights
+ /// Contacts
+ ///
+ ///
+ ///
+ /// User list
+ [Read(@"contact/{contactid:int}/access")]
+ public IEnumerable GetContactAccessList(int contactid)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+
+ if (contact == null) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanAccessTo(contact)) throw _crmSecurity.CreateSecurityException();
+
+ return _crmSecurity.IsPrivate(contact)
+ ? _crmSecurity.GetAccessSubjectTo(contact)
+ .Select(item => _employeeWraperHelper.Get(item.Key))
+ : new List();
+ }
+
+ ///
+ /// Sets access rights for other users to the contact with the ID specified in the request
+ ///
+ /// Contact ID
+ /// Contact privacy: private or not
+ /// List of managers
+ /// Set contact access rights
+ /// Contacts
+ ///
+ ///
+ ///
+ ///
+ /// Contact
+ ///
+ [Update(@"contact/{contactid:int}/access")]
+ public ContactDto SetAccessToContact(int contactid, bool isShared, IEnumerable managerList)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().GetByID(contactid);
+ if (contact == null) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanEdit(contact)) throw _crmSecurity.CreateSecurityException();
+
+ SetAccessToContact(contact, isShared, managerList, false);
+
+ var wrapper = _mapper.Map(contact);
+
+ return wrapper;
+ }
+
+ private void SetAccessToContact(Contact contact, bool isShared, IEnumerable managerList, bool isNotify)
+ {
+ var managerListLocal = managerList != null ? managerList.Distinct().ToList() : new List();
+ if (managerListLocal.Any())
+ {
+ if (isNotify)
+ {
+ var notifyUsers = managerListLocal.Where(n => n != _securityContext.CurrentAccount.ID).ToArray();
+
+ if (contact is Person)
+ _notifyClient.SendAboutSetAccess(EntityType.Person, contact.ID, _daoFactory, notifyUsers);
+ else
+ _notifyClient.SendAboutSetAccess(EntityType.Company, contact.ID, _daoFactory, notifyUsers);
+
+ }
+
+ _crmSecurity.SetAccessTo(contact, managerListLocal);
+ }
+ else
+ {
+ _crmSecurity.MakePublic(contact);
+ }
+
+ _daoFactory.GetContactDao().MakePublic(contact.ID, isShared);
+ }
+
+ ///
+ /// Sets access rights for other users to the list of contacts with the IDs specified in the request
+ ///
+ /// Contact ID list
+ /// Company privacy: shared or not
+ /// List of managers
+ /// Set contact access rights
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Contact list
+ ///
+ [Update(@"contact/access")]
+ public IEnumerable SetAccessToBatchContact(
+ [FromBody] SetAccessToBatchContactRequestDto inDto)
+ {
+ var contactid = inDto.ContactID;
+ var isShared = inDto.isShared;
+ var managerList = inDto.ManagerList;
+
+ if (contactid == null) throw new ArgumentException();
+
+ var result = new List();
+
+ foreach (var id in contactid)
+ {
+ var contactDto = SetAccessToContact(id, isShared, managerList);
+ result.Add(contactDto);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Sets access rights for the selected user to the list of contacts with the parameters specified in the request
+ ///
+ /// Contact privacy: private or not
+ /// List of managers
+ /// Tag
+ /// Contact stage ID (warmth)
+ /// Contact type ID
+ ///
+ /// Start date
+ /// End date
+ /// Set contact access rights
+ /// Contacts
+ ///
+ ///
+ ///
+ /// Contact list
+ ///
+ [Update(@"contact/filter/access")]
+ public IEnumerable SetAccessToBatchContact(
+ [FromForm] SetAccessToBatchContactByFilterRequestDto inDto)
+ {
+ IEnumerable tags = inDto.Tags;
+ int? contactStage = inDto.ContactStage;
+ int? contactType = inDto.ContactType;
+ ContactListViewType contactListView = inDto.ContactListView;
+ ApiDateTime fromDate = inDto.FromDate;
+ ApiDateTime toDate = inDto.ToDate;
+ bool isPrivate = inDto.isPrivate;
+ IEnumerable managerList = inDto.ManagerList;
+
+ int contactStageInt = contactStage.HasValue ? contactStage.Value : -1;
+ int contactTypeInt = contactType.HasValue ? contactType.Value : -1;
+
+ var result = new List();
+
+ var contacts = _daoFactory.GetContactDao().GetContacts(
+ _apiContext.FilterValue,
+ tags,
+ contactStageInt,
+ contactTypeInt,
+ contactListView,
+ fromDate, toDate,
+ 0, 0, null);
+
+ if (!contacts.Any())
+ return Enumerable.Empty();
+
+ foreach (var contact in contacts)
+ {
+ if (contact == null)
+ throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanEdit(contact)) continue;
+
+ SetAccessToContact(contact, isPrivate, managerList, false);
+
+ result.Add(contact);
+ }
+ return _mapper.Map, List>(result);
+ }
+
+ ///
+ /// Deletes the contact with the ID specified in the request from the portal
+ ///
+ /// Delete contact
+ /// Contacts
+ /// Contact ID
+ ///
+ ///
+ ///
+ /// Contact
+ ///
+ [Delete(@"contact/{contactid:int}")]
+ public ContactDto DeleteContact(int contactid)
+ {
+ if (contactid <= 0) throw new ArgumentException();
+
+ var contact = _daoFactory.GetContactDao().DeleteContact(contactid);
+ if (contact == null) throw new ItemNotFoundException();
+
+ var messageAction = contact is Person ? MessageAction.PersonDeleted : MessageAction.CompanyDeleted;
+ _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle());
+
+ return _mapper.Map(contact);
+ }
+
+ ///
+ /// Deletes the group of contacts with the IDs specified in the request
+ ///
+ /// Contact ID list
+ ///
+ ///
+ /// Delete contact group
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ [Update(@"contact")]
+ public IEnumerable DeleteBatchContacts(IEnumerable contactids)
+ {
+ if (contactids == null) throw new ArgumentException();
+
+ var contacts = _daoFactory.GetContactDao().DeleteBatchContact(contactids.ToArray());
+ _messageService.Send(MessageAction.ContactsDeleted, _messageTarget.Create(contactids), contacts.Select(c => c.GetTitle()));
+
+ return contacts.Select(x => _mapper.Map(x));
+ }
+
+ ///
+ /// Returns the list of 30 contacts in the CRM module with prefix
+ ///
+ ///
+ /// searchType
+ ///
+ ///
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ /// false
+ [Read(@"contact/byprefix")]
+ public IEnumerable GetContactsByPrefix(string prefix, int searchType, EntityType entityType, int entityID)
+ {
+ var result = new List();
+ var allContacts = new List();
+
+ if (entityID > 0)
+ {
+ var findedContacts = new List();
+ switch (entityType)
+ {
+ case EntityType.Opportunity:
+ allContacts = _daoFactory.GetContactDao().GetContacts(_daoFactory.GetDealDao().GetMembers(entityID));
+ break;
+ case EntityType.Case:
+ allContacts = _daoFactory.GetContactDao().GetContacts(_daoFactory.GetCasesDao().GetMembers(entityID));
+ break;
+ }
+
+ foreach (var c in allContacts)
+ {
+ var person = c as Person;
+ if (person != null)
+ {
+ var people = person;
+
+ if (_userFormatter.GetUserName(people.FirstName, people.LastName).IndexOf(prefix, StringComparison.Ordinal) != -1)
+ {
+ findedContacts.Add(person);
+ }
+ }
+ else
+ {
+ var company = (Company)c;
+ if (company.CompanyName.IndexOf(prefix, StringComparison.Ordinal) != -1)
+ {
+ findedContacts.Add(c);
+ }
+ }
+ }
+ result.AddRange(findedContacts.Select(x => _mapper.Map(x)));
+
+ _apiContext.SetTotalCount(findedContacts.Count);
+
+ }
+ else
+ {
+ const int maxItemCount = 30;
+
+ if (searchType < -1 || searchType > 3) throw new ArgumentException();
+
+ allContacts = _daoFactory.GetContactDao().GetContactsByPrefix(prefix, searchType, 0, maxItemCount);
+
+ result.AddRange(allContacts.Select(x => _mapper.Map(x)));
+
+ }
+
+ return result;
+ }
+
+
+ ///
+ /// Returns the list contacts in the CRM module with contact information
+ ///
+ /// Contact information type
+ /// Data
+ /// Category
+ /// Contact importance: primary or not
+ /// Contacts
+ ///
+ /// Contact list
+ ///
+ [Read(@"contact/bycontactinfo")]
+ public IEnumerable GetContactsByContactInfo(ContactInfoType? infoType, String data, int? category, bool? isPrimary)
+ {
+ if (!infoType.HasValue) throw new ArgumentException();
+
+ var ids = _daoFactory.GetContactDao().GetContactIDsByContactInfo(infoType.Value, data, category, isPrimary);
+
+ var result = _daoFactory.GetContactDao().GetContacts(ids.ToArray()).ConvertAll(x => _mapper.Map(x));
+
+ return result;
+ }
+
+ /////
+ /////
+ /////
+ /////
+ /////
+ ///// Contacts
+ /////
+ //[Read(@"contact/{contactid:int}/tweets")]
+ //public List GetUserTweets(int contactid, int count)
+ //{
+ // var MessageCount = 10;
+ // var twitterAccounts = DaoFactory.GetContactInfoDao().GetList(contactid, ContactInfoType.Twitter, null, null);
+
+ // if (twitterAccounts.Count == 0)
+ // throw new ResourceNotFoundException(
+ // Newtonsoft.Json.JsonConvert.SerializeObject(
+ // new
+ // {
+ // message = "",
+ // description = CRMSocialMediaResource.SocialMediaAccountNotFoundTwitter
+ // }
+ // ));
+
+ // var apiInfo = TwitterApiHelper.GetTwitterApiInfoForCurrentUser();
+ // TwitterDataProvider twitterProvider = new TwitterDataProvider(apiInfo);
+
+ // List messages = new List();
+
+ // foreach (var twitterAccount in twitterAccounts)
+ // {
+ // try
+ // {
+ // messages.AddRange(twitterProvider.GetUserTweets(twitterAccount.ID, twitterAccount.Data, MessageCount));
+ // }
+ // catch (ResourceNotFoundException ex)
+ // {
+ // throw new ResourceNotFoundException(
+ // Newtonsoft.Json.JsonConvert.SerializeObject(
+ // new
+ // {
+ // message = ex.Message,
+ // description = String.Format("{0}: {1}", CRMSocialMediaResource.ErrorUnknownTwitterAccount, twitterAccount.Data)
+ // }
+ // ));
+ // }
+ // catch (Exception ex)
+ // {
+ // throw new Exception(
+ // Newtonsoft.Json.JsonConvert.SerializeObject(
+ // new
+ // {
+ // message = ex.Message,
+ // description = String.Format("{0}: {1}", CRMSocialMediaResource.ErrorUnknownTwitterAccount, twitterAccount.Data)
+ // }
+ // ));
+ // }
+ // }
+
+
+ // return messages.OrderByDescending(m => m.PostedOn).Take(MessageCount).ToList();
+
+ //}
+
+
+ /////
+ /////
+ /////
+ /////
+ ///// Contacts
+ /////
+ //[Read(@"contact/twitterprofile")]
+ //public List FindTwitterProfiles(string searchText)
+ //{
+ // try
+ // {
+ // TwitterApiInfo apiInfo = TwitterApiHelper.GetTwitterApiInfoForCurrentUser();
+ // if (apiInfo == null)
+ // throw new SocialMediaAccountNotFound(CRMSocialMediaResource.SocialMediaAccountNotFoundTwitter);
+
+ // TwitterDataProvider provider = new TwitterDataProvider(apiInfo);
+ // List users = provider.FindUsers(searchText);
+ // /*List users = new List();
+ // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 });
+ // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 });
+ // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 });*/
+ // return users;
+ // }
+ // catch (Exception ex) {
+ // throw new SocialMediaUI(DaoFactory).ProcessError(ex, "ASC.CRM.Api.CRMApi.FindTwitterProfiles");
+ // }
+ //}
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Contacts
+ ///
+ [Delete(@"contact/{contactid:int}/avatar")]
+ public string DeleteContactAvatar(int contactId, string contactType, bool uploadOnly)
+ {
+ bool isCompany;
+
+ if (contactId != 0)
+ {
+ var contact = _daoFactory.GetContactDao().GetByID(contactId);
+ if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ if (!_crmSecurity.CanEdit(contact)) throw _crmSecurity.CreateSecurityException();
+
+ isCompany = contact is Company;
+ }
+ else
+ {
+ isCompany = contactType != "people";
+ }
+
+ if (!uploadOnly)
+ {
+ _contactPhotoManager.DeletePhoto(contactId);
+ return _contactPhotoManager.GetBigSizePhoto(0, isCompany);
+ }
+ return "";
+ }
+
+ /////
+ /////
+ /////
+ /////
+ ///// Contacts
+ /////
+ //[Read(@"contact/{contactid:int}/socialmediaavatar")]
+ //public List GetContactSMImages(int contactId)
+ //{
+ // return new SocialMediaUI(DaoFactory).GetContactSMImages(contactId);
+ //}
+
+ /////
+ /////
+ /////
+ /////
+ ///// Contacts
+ /////
+ //[Create(@"contact/socialmediaavatar")]
+ //public List GetContactSMImagesByNetworks(List socialNetworks)
+ //{
+ // if (socialNetworks == null || socialNetworks.Count == 0)
+ // {
+ // return new List();
+ // }
+ // var twitter = new List();
+
+ // foreach (var sn in socialNetworks)
+ // {
+ // if (sn.InfoType == ContactInfoType.Twitter) twitter.Add(sn.Data);
+ // }
+
+ // return new SocialMediaUI(DaoFactory).GetContactSMImages(twitter);
+ //}
+
+ /////
+ /////
+ /////
+ /////
+ /////
+ /////
+ /////
+ /////
+ ///// Contacts
+ /////
+ //[Update(@"contact/{contactid:int}/avatar")]
+ //public ContactPhotoManager.PhotoData UploadUserAvatarFromSocialNetwork(int contactId, SocialNetworks socialNetwork, string userIdentity, bool uploadOnly, string tmpDirName)
+ //{
+ // if (socialNetwork != SocialNetworks.Twitter)
+ // throw new ArgumentException();
+
+ // if (contactId != 0)
+ // {
+ // var contact = DaoFactory.GetContactDao().GetByID(contactId);
+ // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException();
+
+ // if (!CRMSecurity.CanEdit(contact)) throw CRMSecurity.CreateSecurityException();
+ // }
+
+ // if (socialNetwork == SocialNetworks.Twitter)
+ // {
+ // var provider = new TwitterDataProvider(TwitterApiHelper.GetTwitterApiInfoForCurrentUser());
+ // var imageUrl = provider.GetUrlOfUserImage(userIdentity, TwitterDataProvider.ImageSize.Original);
+ // return UploadAvatar(contactId, imageUrl, uploadOnly, tmpDirName, false);
+ // }
+
+ // return null;
+ //}
+
+ /// false
+ [Create(@"contact/mailsmtp/send")]
+ public IProgressItem SendMailSMTPToContacts(
+ SendMailSMTPToContactsRequestDto inDto)
+ {
+ List fileIDs = inDto.FileIDs;
+ List contactIds = inDto.ContactIds;
+ String subject = inDto.Subject;
+ String body = inDto.Body;
+ bool storeInHistory = inDto.StoreInHistory;
+
+ if (contactIds == null || contactIds.Count == 0 || String.IsNullOrEmpty(body)) throw new ArgumentException();
+
+ var contacts = _daoFactory.GetContactDao().GetContacts(contactIds.ToArray());
+
+ _messageService.Send(MessageAction.CrmSmtpMailSent, _messageTarget.Create(contactIds), contacts.Select(c => c.GetTitle()));
+
+ return _mailSender.Start(fileIDs, contactIds, subject, body, storeInHistory);
+ }
+
+ /// false
+ [Create(@"contact/mailsmtp/preview")]
+ public string GetMailSMTPToContactsPreview([FromForm] string template, [FromForm] int contactId)
+ {
+ if (contactId == 0 || String.IsNullOrEmpty(template)) throw new ArgumentException();
+
+ var manager = new MailTemplateManager(_daoFactory);
+
+ return manager.Apply(template, contactId);
+ }
+
+ /// false
+ [Read(@"contact/mailsmtp/status")]
+ public IProgressItem GetMailSMTPToContactsStatus()
+ {
+ return _mailSender.GetStatus();
+ }
+
+ /// false
+ [Update(@"contact/mailsmtp/cancel")]
+ public IProgressItem CancelMailSMTPToContacts()
+ {
+ var progressItem = _mailSender.GetStatus();
+
+ _mailSender.Cancel();
+
+ return progressItem;
+
+ }
+
+ /// false
+ [Update(@"contact/{contactid:int}/creationdate")]
+ public void SetContactCreationDate(int contactId, ApiDateTime creationDate)
+ {
+ var dao = _daoFactory.GetContactDao();
+ var contact = dao.GetByID(contactId);
+
+ if (contact == null || !_crmSecurity.CanAccessTo(contact))
+ throw new ItemNotFoundException();
+
+ dao.SetContactCreationDate(contactId, creationDate);
+ }
+
+ /// false
+ [Update(@"contact/{contactid:int}/lastmodifeddate")]
+ public void SetContactLastModifedDate(int contactId, ApiDateTime lastModifedDate)
+ {
+ var dao = _daoFactory.GetContactDao();
+ var contact = dao.GetByID(contactId);
+
+ if (contact == null || !_crmSecurity.CanAccessTo(contact))
+ throw new ItemNotFoundException();
+
+ dao.SetContactLastModifedDate(contactId, lastModifedDate);
+ }
+
+
+ private ContactPhotoManager.PhotoData UploadAvatar(int contactID, string imageUrl, bool uploadOnly, string tmpDirName, bool checkFormat = true)
+ {
+ if (contactID != 0)
+ {
+ return _contactPhotoManager.UploadPhoto(imageUrl, contactID, uploadOnly, checkFormat);
+ }
+
+ if (string.IsNullOrEmpty(tmpDirName) || tmpDirName == "null") tmpDirName = null;
+ return _contactPhotoManager.UploadPhotoToTemp(imageUrl, tmpDirName, checkFormat);
+ }
+
+ private IEnumerable ToSimpleListContactDto(IReadOnlyList itemList)
+ {
+ if (itemList.Count == 0) return new List();
+
+ var result = new List();
+
+ var personsIDs = new List();
+ var companyIDs = new List();
+ var contactIDs = new int[itemList.Count];
+
+ var peopleCompanyIDs = new List();
+ var peopleCompanyList = new Dictionary();
+
+ var contactDao = _daoFactory.GetContactDao();
+
+ for (var index = 0; index < itemList.Count; index++)
+ {
+ var contact = itemList[index];
+
+ if (contact is Company)
+ {
+ companyIDs.Add(contact.ID);
+ }
+ else
+ {
+ var person = contact as Person;
+ if (person != null)
+ {
+ personsIDs.Add(person.ID);
+
+ if (person.CompanyID > 0)
+ {
+ peopleCompanyIDs.Add(person.CompanyID);
+ }
+ }
+ }
+
+ contactIDs[index] = itemList[index].ID;
+ }
+
+ if (peopleCompanyIDs.Count > 0)
+ {
+ var tmpList = contactDao.GetContacts(peopleCompanyIDs.ToArray()).ConvertAll(item => _mapper.Map(item));
+ var tmpListCanDelete = contactDao.CanDelete(tmpList.Select(item => item.Id).ToArray());
+
+ foreach (var contactBaseDtoQuick in tmpList)
+ {
+ contactBaseDtoQuick.CanDelete = contactBaseDtoQuick.CanEdit && tmpListCanDelete[contactBaseDtoQuick.Id];
+ peopleCompanyList.Add(contactBaseDtoQuick.Id, contactBaseDtoQuick);
+ }
+ }
+
+ var contactInfos = new Dictionary>();
+
+ var addresses = new Dictionary>();
+
+ _daoFactory.GetContactInfoDao().GetAll(contactIDs).ForEach(
+ item =>
+ {
+ if (item.InfoType == ContactInfoType.Address)
+ {
+ if (!addresses.ContainsKey(item.ContactID))
+ {
+ addresses.Add(item.ContactID, new List
+ {
+ new Address(item)
+ });
+ }
+ else
+ {
+ addresses[item.ContactID].Add(new Address(item));
+ }
+ }
+ else
+ {
+ if (!contactInfos.ContainsKey(item.ContactID))
+ {
+ contactInfos.Add(item.ContactID, new List { _mapper.Map(item) });
+ }
+ else
+ {
+ contactInfos[item.ContactID].Add(_mapper.Map(item));
+ }
+ }
+ }
+ );
+
+ var nearestTasks = _daoFactory.GetTaskDao().GetNearestTask(contactIDs.ToArray());
+
+ IEnumerable taskCategories = new List();
+
+ if (nearestTasks.Any())
+ {
+ taskCategories = _daoFactory.GetListItemDao().GetItems(ListType.TaskCategory).ConvertAll(item => _mapper.Map(item));
+ }
+
+ foreach (var contact in itemList)
+ {
+ ContactDto contactDto;
+
+ var person = contact as Person;
+ if (person != null)
+ {
+ var people = person;
+
+ var peopleDto = _mapper.Map(people);
+
+ if (people.CompanyID > 0 && peopleCompanyList.ContainsKey(people.CompanyID))
+ {
+ peopleDto.Company = peopleCompanyList[people.CompanyID];
+ }
+
+ contactDto = peopleDto;
+ }
+ else
+ {
+ var company = contact as Company;
+ if (company != null)
+ {
+ contactDto = _mapper.Map(company);
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+
+ contactDto.CommonData = contactInfos.ContainsKey(contact.ID) ? contactInfos[contact.ID] : new List();
+
+ TaskBaseDto taskDto = null;
+
+ if (nearestTasks.ContainsKey(contactDto.Id))
+ {
+ var task = nearestTasks[contactDto.Id];
+
+ taskDto = _mapper.Map(task);
+
+ if (task.CategoryID > 0)
+ {
+ taskDto.Category = taskCategories.First(x => x.Id == task.CategoryID);
+ }
+ }
+
+ result.Add(new ContactWithTaskDto
+ {
+ Contact = contactDto,
+ Task = taskDto
+ });
+ }
+
+
+ #region CanDelete for main contacts
+
+ if (result.Count > 0)
+ {
+ var resultListCanDelete = contactDao.CanDelete(result.Select(item => item.Contact.Id).ToArray());
+ foreach (var contactBaseDtoQuick in result)
+ {
+ contactBaseDtoQuick.Contact.CanDelete = contactBaseDtoQuick.Contact.CanEdit && resultListCanDelete[contactBaseDtoQuick.Contact.Id];
+ }
+ }
+
+ #endregion
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/Server/Api/CurrencyRatesController.cs b/products/ASC.CRM/Server/Api/CurrencyRatesController.cs
new file mode 100644
index 00000000000..6ac52f9b239
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/CurrencyRatesController.cs
@@ -0,0 +1,331 @@
+/*
+ *
+ * (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.Api.CRM;
+using ASC.Core.Common.Settings;
+using ASC.CRM.ApiModels;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Resources;
+using ASC.MessagingSystem;
+using ASC.Web.Api.Routing;
+using ASC.Web.CRM.Classes;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace ASC.CRM.Api
+{
+ public class CurrencyRatesController : BaseApiController
+ {
+ private readonly CurrencyProvider _currencyProvider;
+ private readonly SettingsManager _settingsManager;
+ private readonly Global _global;
+ private readonly MessageService _messageService;
+
+ public CurrencyRatesController(CrmSecurity crmSecurity,
+ DaoFactory daoFactory,
+ MessageService messageService,
+ SettingsManager settingsManager,
+ Global global,
+ CurrencyProvider currencyProvider,
+ IMapper mapper)
+ : base(daoFactory, crmSecurity, mapper)
+ {
+ _messageService = messageService;
+ _settingsManager = settingsManager;
+ _global = global;
+ _currencyProvider = currencyProvider;
+ }
+
+
+ //TABLE `crm_currency_rate` column `rate` DECIMAL(10,2) NOT NULL
+ public const decimal MaxRateValue = (decimal)99999999.99;
+
+ ///
+ /// Get the list of currency rates
+ ///
+ /// Get currency rates list
+ /// Common
+ ///
+ /// List of currency rates
+ ///
+ [Read(@"currency/rates")]
+ public IEnumerable GetCurrencyRates()
+ {
+ return _daoFactory.GetCurrencyRateDao().GetAll().ConvertAll(x => _mapper.Map(x));
+ }
+
+ ///
+ /// Get currency rate by id
+ ///
+ /// Get currency rate
+ /// Common
+ ///
+ /// Currency rate
+ ///
+ ///
+ [Read(@"currency/rates/{id:int}")]
+ public CurrencyRateDto GetCurrencyRate(int id)
+ {
+ if (id <= 0) throw new ArgumentException();
+
+ var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id);
+
+ return _mapper.Map(currencyRate);
+ }
+
+ ///
+ /// Get currency rate by currencies
+ ///
+ /// Get currency rate
+ /// Common
+ ///
+ /// Currency rate
+ ///
+ ///
+ [Read(@"currency/rates/{fromCurrency}/{toCurrency}")]
+ public CurrencyRateDto GetCurrencyRate(string fromCurrency, string toCurrency)
+ {
+ if (string.IsNullOrEmpty(fromCurrency) || string.IsNullOrEmpty(toCurrency))
+ throw new ArgumentException();
+
+ var currencyRate = _daoFactory.GetCurrencyRateDao().GetByCurrencies(fromCurrency, toCurrency);
+
+ return _mapper.Map(currencyRate);
+ }
+
+ ///
+ /// Create new currency rate object
+ ///
+ ///
+ /// Common
+ ///
+ [Create(@"currency/rates")]
+ public CurrencyRateDto CreateCurrencyRate(
+ [FromForm] string fromCurrency,
+ [FromForm] string toCurrency,
+ [FromForm] decimal rate)
+ {
+ ValidateRate(rate);
+
+ ValidateCurrencies(new[] { fromCurrency, toCurrency });
+
+ var currencyRate = new CurrencyRate
+ {
+ FromCurrency = fromCurrency,
+ ToCurrency = toCurrency,
+ Rate = rate
+ };
+
+ currencyRate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(currencyRate);
+ _messageService.Send(MessageAction.CurrencyRateUpdated, fromCurrency, toCurrency);
+
+ return _mapper.Map(currencyRate);
+ }
+
+ ///
+ /// Update currency rate object
+ ///
+ ///
+ /// Common
+ ///
+ [Update(@"currency/rates/{id:int}")]
+ public CurrencyRateDto UpdateCurrencyRate(int id, string fromCurrency, string toCurrency, decimal rate)
+ {
+ if (id <= 0)
+ throw new ArgumentException();
+
+ ValidateRate(rate);
+
+ ValidateCurrencies(new[] { fromCurrency, toCurrency });
+
+ var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id);
+
+ if (currencyRate == null)
+ throw new ArgumentException();
+
+ currencyRate.FromCurrency = fromCurrency;
+ currencyRate.ToCurrency = toCurrency;
+ currencyRate.Rate = rate;
+
+ currencyRate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(currencyRate);
+ _messageService.Send(MessageAction.CurrencyRateUpdated, fromCurrency, toCurrency);
+
+ return _mapper.Map(currencyRate);
+ }
+
+ ///
+ /// Set currency rates
+ ///
+ ///
+ /// Common
+ ///
+ [Create(@"currency/setrates")]
+ public List SetCurrencyRates(
+ [FromForm] String currency,
+ [FromForm] List rates)
+ {
+ if (!_crmSecurity.IsAdmin)
+ throw _crmSecurity.CreateSecurityException();
+
+ if (string.IsNullOrEmpty(currency))
+ throw new ArgumentException();
+
+ ValidateCurrencyRates(rates);
+
+ currency = currency.ToUpper();
+
+ var crmSettings = _settingsManager.Load();
+ var defaultCurrency = _currencyProvider.Get(_settingsManager.Load().DefaultCurrency);
+
+ if (defaultCurrency.Abbreviation != currency)
+ {
+ var cur = _currencyProvider.Get(currency);
+
+ if (cur == null)
+ throw new ArgumentException();
+
+ _global.SaveDefaultCurrencySettings(cur);
+
+ _messageService.Send(MessageAction.CrmDefaultCurrencyUpdated);
+ }
+
+ rates = _daoFactory.GetCurrencyRateDao().SetCurrencyRates(rates);
+
+ foreach (var rate in rates)
+ {
+ _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency);
+ }
+
+ return rates.Select(x => _mapper.Map(x)).ToList();
+ }
+
+ ///
+ /// Add currency rates
+ ///
+ ///
+ /// Common
+ ///
+ [Create(@"currency/addrates")]
+ public List AddCurrencyRates([FromForm] List rates)
+ {
+ if (!_crmSecurity.IsAdmin)
+ throw _crmSecurity.CreateSecurityException();
+
+ ValidateCurrencyRates(rates);
+
+ var existingRates = _daoFactory.GetCurrencyRateDao().GetAll();
+
+ foreach (var rate in rates)
+ {
+ var exist = false;
+
+ foreach (var existingRate in existingRates)
+ {
+ if (rate.FromCurrency != existingRate.FromCurrency || rate.ToCurrency != existingRate.ToCurrency)
+ continue;
+
+ existingRate.Rate = rate.Rate;
+ _daoFactory.GetCurrencyRateDao().SaveOrUpdate(existingRate);
+ _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency);
+ exist = true;
+ break;
+ }
+
+ if (exist) continue;
+
+ rate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(rate);
+ _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency);
+ existingRates.Add(rate);
+ }
+
+ return existingRates.Select(x => _mapper.Map(x)).ToList();
+ }
+
+ ///
+ /// Delete currency rate object
+ ///
+ ///
+ /// Common
+ ///
+ [Delete(@"currency/rates/{id:int}")]
+ public CurrencyRateDto DeleteCurrencyRate(int id)
+ {
+ if (id <= 0)
+ throw new ArgumentException();
+
+ var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id);
+
+ if (currencyRate == null)
+ throw new ArgumentException();
+
+ _daoFactory.GetCurrencyRateDao().Delete(id);
+
+ return _mapper.Map(currencyRate);
+ }
+
+ private void ValidateCurrencyRates(IEnumerable rates)
+ {
+ var currencies = new List();
+
+ foreach (var rate in rates)
+ {
+ ValidateRate(rate.Rate);
+ currencies.Add(rate.FromCurrency);
+ currencies.Add(rate.ToCurrency);
+ }
+
+ ValidateCurrencies(currencies.ToArray());
+ }
+
+ private void ValidateCurrencies(string[] currencies)
+ {
+ if (currencies.Any(string.IsNullOrEmpty))
+ throw new ArgumentException();
+
+ var available = _currencyProvider.GetAll().Select(x => x.Abbreviation);
+
+ var unknown = currencies.Where(x => !available.Contains(x)).ToArray();
+
+ if (!unknown.Any()) return;
+
+ throw new ArgumentException(string.Format(CRMErrorsResource.UnknownCurrency, string.Join(",", unknown)));
+ }
+
+ private static void ValidateRate(decimal rate)
+ {
+ if (rate < 0 || rate > MaxRateValue)
+ throw new ArgumentException(string.Format(CRMErrorsResource.InvalidCurrencyRate, rate));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/products/ASC.CRM/Server/Api/CustomFieldsController.cs b/products/ASC.CRM/Server/Api/CustomFieldsController.cs
new file mode 100644
index 00000000000..dafe225a685
--- /dev/null
+++ b/products/ASC.CRM/Server/Api/CustomFieldsController.cs
@@ -0,0 +1,428 @@
+/*
+ *
+ * (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.Api.CRM;
+using ASC.Common.Web;
+using ASC.CRM.ApiModels;
+using ASC.CRM.Core;
+using ASC.CRM.Core.Dao;
+using ASC.CRM.Core.Entities;
+using ASC.CRM.Core.Enums;
+using ASC.MessagingSystem;
+using ASC.Web.Api.Routing;
+
+using AutoMapper;
+
+using Microsoft.AspNetCore.Mvc;
+
+namespace ASC.CRM.Api
+{
+ public class CustomFieldsController : BaseApiController
+ {
+ private readonly MessageService _messageService;
+ private readonly MessageTarget _messageTarget;
+
+ public CustomFieldsController(CrmSecurity crmSecurity,
+ DaoFactory daoFactory,
+ MessageTarget messageTarget,
+ MessageService messageService,
+ IMapper mapper)
+ : base(daoFactory, crmSecurity, mapper)
+ {
+ _messageTarget = messageTarget;
+ _messageService = messageService;
+ }
+
+
+ ///
+ /// Returns the list of descriptions for all existing user fields
+ ///
+ /// Type
+ /// Get user field list
+ /// User fields
+ ///
+ /// User field list
+ ///
+ ///
+ [Read(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/definitions")]
+ public IEnumerable GetCustomFieldDefinitions(string entityType)
+ {
+ return _mapper.Map, List>(_daoFactory.GetCustomFieldDao().GetFieldsDescription(ToEntityType(entityType)));
+ }
+
+ ///
+ /// Returns the list of all user field values using the entity type and entity ID specified in the request
+ ///
+ /// Type
+ /// ID
+ /// Get user field values
+ /// User fields
+ ///
+ [Read(@"{entityType:regex(contact|person|company|opportunity|case)}/{entityid:int}/customfield")]
+ public IEnumerable GetCustomFieldForSubject(string entityType, int entityid)
+ {
+ return _mapper.Map, List>(_daoFactory.GetCustomFieldDao().GetEnityFields(ToEntityType(entityType), entityid, false));
+ }
+
+ ///
+ /// Sets the new user field value using the entity type, ID, field ID and value specified in the request
+ ///
+ /// Type
+ /// ID
+ /// Field ID
+ /// Field Value
+ /// Set user field value
+ /// User fields
+ ///
+ /// User field
+ ///
+ [Create(@"{entityType:regex(contact|person|company|opportunity|case)}/{entityid:int}/customfield/{fieldid:int}")]
+ public CustomFieldBaseDto SetEntityCustomFieldValue(
+ [FromRoute] string entityType,
+ [FromRoute] int entityid,
+ [FromRoute] int fieldid,
+ [FromForm] string fieldValue)
+ {
+ var customField = _daoFactory.GetCustomFieldDao().GetFieldDescription(fieldid);
+
+ var entityTypeStr = ToEntityType(entityType);
+
+ customField.EntityID = entityid;
+ customField.Value = fieldValue;
+
+ _daoFactory.GetCustomFieldDao().SetFieldValue(entityTypeStr, entityid, fieldid, fieldValue);
+
+ return _mapper.Map(customField);
+ }
+
+ ///
+ /// Creates a new user field with the parameters (entity type, field title, type, etc.) specified in the request
+ ///
+ /// Entity type
+ /// Field title
+ ///
+ /// User field value
+ ///
+ /// Field position
+ /// Mask
+ /// Create user field
+ /// User fields
+ ///
+ /// User field
+ ///
+ ///
+ ///
+ ///
+ [Create(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield")]
+ public CustomFieldDto CreateCustomFieldValue(
+ [FromRoute] string entityType,
+ [FromForm] string label,
+ [FromForm] int fieldType,
+ [FromForm] int position,
+ [FromForm] string mask)
+ {
+ if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException();
+ var entityTypeObj = ToEntityType(entityType);
+ var fieldID = _daoFactory.GetCustomFieldDao().CreateField(entityTypeObj, label, (CustomFieldType)fieldType, mask);
+ var wrapper = _daoFactory.GetCustomFieldDao().GetFieldDescription(fieldID);
+
+ var messageAction = GetCustomFieldCreatedAction(entityTypeObj);
+ _messageService.Send(messageAction, _messageTarget.Create(wrapper.ID), wrapper.Label);
+
+ return _mapper.Map(_daoFactory.GetCustomFieldDao().GetFieldDescription(fieldID));
+ }
+
+ ///
+ /// Updates the selected user field with the parameters (entity type, field title, type, etc.) specified in the request
+ ///
+ /// User field id
+ /// Entity type
+ /// Field title
+ ///
+ /// User field value
+ ///
+ /// Field position
+ /// Mask
+ /// Updates the selected user field
+ /// User fields
+ ///
+ /// User field
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [Update(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/{id:int}")]
+ public CustomFieldDto UpdateCustomFieldValue(int id, string entityType, string label, int fieldType, int position, string mask)
+ {
+ if (id <= 0) throw new ArgumentException();
+ if (!_daoFactory.GetCustomFieldDao().IsExist(id)) throw new ItemNotFoundException();
+
+ var entityTypeObj = ToEntityType(entityType);
+
+ var customField = new CustomField
+ {
+ EntityType = entityTypeObj,
+ Type = (CustomFieldType)fieldType,
+ ID = id,
+ Mask = mask,
+ Label = label,
+ SortOrder = position
+ };
+
+ _daoFactory.GetCustomFieldDao().EditItem(customField);
+
+ customField = _daoFactory.GetCustomFieldDao().GetFieldDescription(id);
+
+ var messageAction = GetCustomFieldUpdatedAction(entityTypeObj);
+ _messageService.Send(messageAction, _messageTarget.Create(customField.ID), customField.Label);
+
+ return _mapper.Map(customField);
+ }
+
+ ///