diff --git a/VirtoCommerce.CoreModule.Data/Notifications/EmailConfirmationNotification.cs b/VirtoCommerce.CoreModule.Data/Notifications/EmailConfirmationNotification.cs new file mode 100644 index 00000000..d2ddd812 --- /dev/null +++ b/VirtoCommerce.CoreModule.Data/Notifications/EmailConfirmationNotification.cs @@ -0,0 +1,15 @@ +using VirtoCommerce.Platform.Core.Notifications; + +namespace VirtoCommerce.CoreModule.Data.Notifications +{ + public class EmailConfirmationNotification : EmailNotification + { + public EmailConfirmationNotification(IEmailNotificationSendingGateway gateway) + : base(gateway) + { + } + + [NotificationParameter("E-mail confirmation URL")] + public string Url { get; set; } + } +} diff --git a/VirtoCommerce.CoreModule.Data/VirtoCommerce.CoreModule.Data.csproj b/VirtoCommerce.CoreModule.Data/VirtoCommerce.CoreModule.Data.csproj index 6719dece..ede5d483 100644 --- a/VirtoCommerce.CoreModule.Data/VirtoCommerce.CoreModule.Data.csproj +++ b/VirtoCommerce.CoreModule.Data/VirtoCommerce.CoreModule.Data.csproj @@ -133,6 +133,7 @@ + diff --git a/VirtoCommerce.CoreModule.Web/Controllers/Api/StoreFrontSecurityController.cs b/VirtoCommerce.CoreModule.Web/Controllers/Api/StoreFrontSecurityController.cs index befaea8d..f7bdeb2a 100644 --- a/VirtoCommerce.CoreModule.Web/Controllers/Api/StoreFrontSecurityController.cs +++ b/VirtoCommerce.CoreModule.Web/Controllers/Api/StoreFrontSecurityController.cs @@ -5,6 +5,7 @@ using System.Web; using System.Web.Http; using System.Web.Http.Description; +using VirtoCommerce.CoreModule.Data.Notifications; using VirtoCommerce.CoreModule.Web.Converters; using VirtoCommerce.CoreModule.Web.Model; using VirtoCommerce.Domain.Customer.Services; @@ -21,14 +22,22 @@ public class StorefrontSecurityController : ApiController { private readonly ISecurityService _securityService; private readonly Func _signInManagerFactory; + private readonly Func _userManagerFactory; private readonly INotificationManager _notificationManager; private readonly IStoreService _storeService; private readonly IMemberService _memberService; - public StorefrontSecurityController(ISecurityService securityService, Func signInManagerFactory, INotificationManager notificationManager, IStoreService storeService, IMemberService memberService) + public StorefrontSecurityController( + ISecurityService securityService, + Func signInManagerFactory, + Func userManagerFactory, + INotificationManager notificationManager, + IStoreService storeService, + IMemberService memberService) { _securityService = securityService; _signInManagerFactory = signInManagerFactory; + _userManagerFactory = userManagerFactory; _notificationManager = notificationManager; _storeService = storeService; _memberService = memberService; @@ -233,7 +242,7 @@ public async Task GenerateResetPasswordToken(string userId, s notification.Sender = store.Email; notification.IsActive = true; - var member = _memberService.GetByIds(new [] { userId }).FirstOrDefault(); + var member = _memberService.GetByIds(new[] { userId }).FirstOrDefault(); if (member != null) { var email = member.Emails.FirstOrDefault(); @@ -270,6 +279,57 @@ public async Task ResetPassword(string userId, string token, return Ok(result); } + [HttpPost] + [Route("user/email/sendconfirmation")] + [ResponseType(typeof(void))] + public async Task SendEmailConfirmation(string userId, string storeName, string language, string callbackUrl) + { + using (var userManager = _userManagerFactory()) + { + var member = _memberService.GetByIds(new[] { userId }).FirstOrDefault(); + if (member != null) + { + var store = _storeService.GetById(storeName); + + var uriBuilder = new UriBuilder(callbackUrl); + var query = HttpUtility.ParseQueryString(uriBuilder.Query); + var token = await userManager.GenerateEmailConfirmationTokenAsync(userId); + query["code"] = token; + uriBuilder.Query = query.ToString(); + + var notification = _notificationManager.GetNewNotification(storeName, "Store", language); + var email = member.Emails.FirstOrDefault(); + if (!string.IsNullOrEmpty(email)) + { + notification.Recipient = email; + } + notification.Url = uriBuilder.ToString(); + notification.Sender = store.Email; + notification.IsActive = true; + _notificationManager.ScheduleSendNotification(notification); + } + } + + return StatusCode(HttpStatusCode.NoContent); + } + + [HttpPost] + [Route("user/email/confirm")] + [ResponseType(typeof(SecurityResult))] + public async Task ConfirmEmail(string userId, string token) + { + if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(token)) + { + return BadRequest(); + } + + using (var userManager = _userManagerFactory()) + { + var result = await userManager.ConfirmEmailAsync(userId, token); + + return Ok(result); + } + } } } diff --git a/VirtoCommerce.CoreModule.Web/Module.cs b/VirtoCommerce.CoreModule.Web/Module.cs index c19c0a27..8cb34eed 100644 --- a/VirtoCommerce.CoreModule.Web/Module.cs +++ b/VirtoCommerce.CoreModule.Web/Module.cs @@ -4,6 +4,7 @@ using Hangfire; using Microsoft.Practices.Unity; using VirtoCommerce.CoreModule.Data.Indexing; +using VirtoCommerce.CoreModule.Data.Notifications; using VirtoCommerce.CoreModule.Data.Observers; using VirtoCommerce.CoreModule.Data.Payment; using VirtoCommerce.CoreModule.Data.Repositories; @@ -14,6 +15,7 @@ using VirtoCommerce.CoreModule.Web.BackgroundJobs; using VirtoCommerce.CoreModule.Web.ExportImport; using VirtoCommerce.CoreModule.Web.JsonConverters; +using VirtoCommerce.CoreModule.Web.Resources; using VirtoCommerce.Domain.Commerce.Model; using VirtoCommerce.Domain.Commerce.Services; using VirtoCommerce.Domain.Customer.Events; @@ -24,6 +26,7 @@ using VirtoCommerce.Platform.Core.Common; using VirtoCommerce.Platform.Core.ExportImport; using VirtoCommerce.Platform.Core.Modularity; +using VirtoCommerce.Platform.Core.Notifications; using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.Platform.Data.Infrastructure; using VirtoCommerce.Platform.Data.Infrastructure.Interceptors; @@ -129,6 +132,20 @@ public override void PostInitialize() var taxService = _container.Resolve(); var paymentService = _container.Resolve(); var moduleSettings = settingsManager.GetModuleSettings("VirtoCommerce.Core"); + var notificationManager = _container.Resolve(); + var emailGateway = _container.Resolve(); + + notificationManager.RegisterNotificationType(() => new EmailConfirmationNotification(emailGateway) + { + DisplayName = "Email confirmation notification", + Description = "This e-mail notification is for confirmation a new registered user e-mail", + NotificationTemplate = new NotificationTemplate + { + Language = "en-US", + Subject = EmailConfirmationResource.Subject, + Body = EmailConfirmationResource.Body + } + }); taxService.RegisterTaxProvider(() => new FixedTaxRateProvider(moduleSettings.First(x => x.Name == "VirtoCommerce.Core.FixedTaxRateProvider.Rate")) { diff --git a/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.Designer.cs b/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.Designer.cs new file mode 100644 index 00000000..8e5f165d --- /dev/null +++ b/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace VirtoCommerce.CoreModule.Web.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class EmailConfirmationResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal EmailConfirmationResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VirtoCommerce.CoreModule.Web.Resources.EmailConfirmationResource", typeof(EmailConfirmationResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to To complete your create account process please click this link: <a href="{{ url }}">{{ url }}</a>. + /// + internal static string Body { + get { + return ResourceManager.GetString("Body", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email confirmation. + /// + internal static string Subject { + get { + return ResourceManager.GetString("Subject", resourceCulture); + } + } + } +} diff --git a/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.resx b/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.resx new file mode 100644 index 00000000..697f36e9 --- /dev/null +++ b/VirtoCommerce.CoreModule.Web/Resources/EmailConfirmationResource.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + To complete your create account process please click this link: <a href="{{ url }}">{{ url }}</a> + + + Email confirmation + + \ No newline at end of file diff --git a/VirtoCommerce.CoreModule.Web/VirtoCommerce.CoreModule.Web.csproj b/VirtoCommerce.CoreModule.Web/VirtoCommerce.CoreModule.Web.csproj index 0a74caac..342cbaf0 100644 --- a/VirtoCommerce.CoreModule.Web/VirtoCommerce.CoreModule.Web.csproj +++ b/VirtoCommerce.CoreModule.Web/VirtoCommerce.CoreModule.Web.csproj @@ -175,6 +175,11 @@ + + True + True + EmailConfirmationResource.resx + @@ -275,6 +280,12 @@ + + + ResXFileCodeGenerator + EmailConfirmationResource.Designer.cs + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/VirtoCommerce.CoreModule.sln b/VirtoCommerce.CoreModule.sln index 1656097d..a2f65267 100644 --- a/VirtoCommerce.CoreModule.sln +++ b/VirtoCommerce.CoreModule.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtoCommerce.CoreModule.Web", "VirtoCommerce.CoreModule.Web\VirtoCommerce.CoreModule.Web.csproj", "{47D7C89C-62A8-44EC-B544-F24E874192AC}" EndProject @@ -18,8 +18,8 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {47D7C89C-62A8-44EC-B544-F24E874192AC}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {47D7C89C-62A8-44EC-B544-F24E874192AC}.Debug|Any CPU.Build.0 = Release|Any CPU + {47D7C89C-62A8-44EC-B544-F24E874192AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47D7C89C-62A8-44EC-B544-F24E874192AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {47D7C89C-62A8-44EC-B544-F24E874192AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {47D7C89C-62A8-44EC-B544-F24E874192AC}.Release|Any CPU.Build.0 = Release|Any CPU {3A627001-02C6-4809-AB42-D517C636E7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU