diff --git a/Client/Index.razor b/Client/Index.razor index 2d30136..6282f6e 100644 --- a/Client/Index.razor +++ b/Client/Index.razor @@ -256,10 +256,18 @@ else { _blogEditor = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList) && PageState.EditMode; - if (PageState.QueryString.ContainsKey("guid")) + if (PageState.QueryString.ContainsKey("guid") && PageState.QueryString.ContainsKey("action")) { - await BlogSubscriberService.DeleteBlogSubscriberAsync(ModuleState.ModuleId, PageState.QueryString["guid"]); - AddModuleMessage(Localizer["Unsubscribed"], MessageType.Success); + if (PageState.QueryString["action"] == "unsubscribe") + { + await BlogSubscriberService.DeleteBlogSubscriberAsync(ModuleState.ModuleId, PageState.QueryString["guid"]); + AddModuleMessage(Localizer["Unsubscribed"], MessageType.Success); + } + if (PageState.QueryString["action"] == "verify") + { + await BlogSubscriberService.UpdateBlogSubscriberAsync(ModuleState.ModuleId, PageState.QueryString["guid"]); + AddModuleMessage(Localizer["Verified"], MessageType.Success); + } } if (PageState.QueryString.ContainsKey("comment") && PageState.QueryString.ContainsKey("created")) diff --git a/Client/ModuleInfo.cs b/Client/ModuleInfo.cs index 95bef44..bb9e14d 100644 --- a/Client/ModuleInfo.cs +++ b/Client/ModuleInfo.cs @@ -9,8 +9,8 @@ public class ModuleInfo : IModule { Name = "Blog", Description = "Blog", - Version = "5.2.0", - ReleaseVersions = "1.0.0,1.0.1,1.0.3,1.0.4,1.0.5,1.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.2.0", + Version = "5.2.1", + ReleaseVersions = "1.0.0,1.0.1,1.0.3,1.0.4,1.0.5,1.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.2.0,5.2.1", ServerManagerType = "Oqtane.Blogs.Manager.BlogManager, Oqtane.Blogs.Server.Oqtane", Dependencies = "Oqtane.Blogs.Shared.Oqtane", SettingsType = "Oqtane.Blogs.Settings, Oqtane.Blogs.Client.Oqtane", diff --git a/Client/Resources/Oqtane.Blogs.Index.resx b/Client/Resources/Oqtane.Blogs.Index.resx index f5ca7a4..af69896 100644 --- a/Client/Resources/Oqtane.Blogs.Index.resx +++ b/Client/Resources/Oqtane.Blogs.Index.resx @@ -139,7 +139,7 @@ Error Loading Blogs - Your Email Subscription Has Been Activated + Your Email Subscription Has Been Received. Please Check Your Email For Further Instructions. Your Email @@ -174,4 +174,7 @@ Manage Categories + + You Have Successfully Verified Your Email Address + \ No newline at end of file diff --git a/Client/Services/BlogSubscriberService.cs b/Client/Services/BlogSubscriberService.cs index 363ac2b..0a16662 100644 --- a/Client/Services/BlogSubscriberService.cs +++ b/Client/Services/BlogSubscriberService.cs @@ -10,7 +10,7 @@ namespace Oqtane.Blogs.Services public interface IBlogSubscriberService { Task AddBlogSubscriberAsync(BlogSubscriber BlogSubscriber); - + Task UpdateBlogSubscriberAsync(int ModuleId, string Guid); Task DeleteBlogSubscriberAsync(int ModuleId, string Guid); } @@ -30,6 +30,11 @@ public async Task AddBlogSubscriberAsync(BlogSubscriber BlogSubscriber) await PostJsonAsync(Apiurl, BlogSubscriber); } + public async Task UpdateBlogSubscriberAsync(int ModuleId, string Guid) + { + await PutAsync($"{Apiurl}/{ModuleId}/{Guid}"); + } + public async Task DeleteBlogSubscriberAsync(int ModuleId, string Guid) { await DeleteAsync($"{Apiurl}/{ModuleId}/{Guid}"); diff --git a/Client/Widgets/ModuleInfo.cs b/Client/Widgets/ModuleInfo.cs index 8824f5a..2fd4035 100644 --- a/Client/Widgets/ModuleInfo.cs +++ b/Client/Widgets/ModuleInfo.cs @@ -9,8 +9,7 @@ public class ModuleInfo : IModule { Name = "Blog Widgets", Description = "Blog Widgets", - Version = "5.2.0", - ReleaseVersions = "5.2.0", + Version = "5.2.1", Dependencies = "Oqtane.Blogs.Shared.Oqtane", SettingsType = "Oqtane.Blogs.Widgets.Settings, Oqtane.Blogs.Client.Oqtane", PackageName = "Oqtane.Blogs.Widgets" diff --git a/Server/Controllers/BlogController.cs b/Server/Controllers/BlogController.cs index 547ad67..e12b4ae 100644 --- a/Server/Controllers/BlogController.cs +++ b/Server/Controllers/BlogController.cs @@ -151,13 +151,16 @@ public int Notify(int id) // this will likely need to be asynchronous in the future as it may timeout foreach (var subscriber in _blogSubscriberRepository.GetBlogSubscribers(blog.ModuleId)) { - var body = blog.PublishedBlogContent.Summary; - body += $"

Read Full Article: {url}"; - var unsubscribe = rooturl + Utilities.NavigateUrl(alias.Path, pagepath, "guid=" + subscriber.Guid); - body += $"

Unsubscribe: {unsubscribe}"; - var notification = new Notification(alias.SiteId, "", sender, "", subscriber.Email, blog.Title, body); - _notificationRepository.AddNotification(notification); - subscribers++; + if (subscriber.IsVerified) + { + var body = blog.PublishedBlogContent.Summary; + body += $"

Read Full Article: {url}"; + var unsubscribe = rooturl + Utilities.NavigateUrl(alias.Path, pagepath, "guid=" + subscriber.Guid + "&action=unsubscribe"); + body += $"

Unsubscribe: {unsubscribe}"; + var notification = new Notification(alias.SiteId, "", sender, "", subscriber.Email, blog.Title, body); + _notificationRepository.AddNotification(notification); + subscribers++; + } } } } diff --git a/Server/Controllers/BlogSubscriberController.cs b/Server/Controllers/BlogSubscriberController.cs index 47612e5..9db1ab2 100644 --- a/Server/Controllers/BlogSubscriberController.cs +++ b/Server/Controllers/BlogSubscriberController.cs @@ -8,6 +8,9 @@ using Oqtane.Controllers; using System.Linq; using System; +using Oqtane.Models; +using Oqtane.Repository; +using Oqtane.Extensions; namespace Oqtane.Blogs.Controllers { @@ -15,28 +18,58 @@ namespace Oqtane.Blogs.Controllers public class BlogSubscriberController : ModuleControllerBase { private readonly IBlogSubscriberRepository _BlogSubscriberRepository; + private readonly ISettingRepository _SettingRepository; + private readonly INotificationRepository _NotificationRepository; - public BlogSubscriberController(IBlogSubscriberRepository BlogSubscriberRepository, ILogManager logger, IHttpContextAccessor accessor) : base(logger,accessor) + public BlogSubscriberController(IBlogSubscriberRepository BlogSubscriberRepository, ISettingRepository SettingRepository, INotificationRepository NotificationRepository, ILogManager logger, IHttpContextAccessor accessor) : base(logger,accessor) { _BlogSubscriberRepository = BlogSubscriberRepository; + _SettingRepository = SettingRepository; + _NotificationRepository = NotificationRepository; } - // POST api//5 + // POST api/ [HttpPost] [IgnoreAntiforgeryToken] public void Post([FromBody] BlogSubscriber BlogSubscriber) { if (ModelState.IsValid && Utilities.IsValidEmail(BlogSubscriber.Email)) { + BlogSubscriber.IsVerified = false; BlogSubscriber.Guid = Guid.NewGuid().ToString(); BlogSubscriber = _BlogSubscriberRepository.AddBlogSubscriber(BlogSubscriber); + if (BlogSubscriber != null) { _logger.Log(LogLevel.Information, this, LogFunction.Create, "Blog Subscriber Added {BlogSubscriber}", BlogSubscriber); + + var settings = _SettingRepository.GetSettings(EntityNames.Module, BlogSubscriber.ModuleId); + var alias = HttpContext.GetAlias(); + var pagepath = settings.First(item => item.SettingName == "PagePath").SettingValue; + var sender = settings.First(item => item.SettingName == "Sender").SettingValue; + var body = "Thank You For Subscribing To Our Blog. Please Verify Your Email Address By Clicking The Link Below."; + var url = alias.Protocol + alias.Name + Utilities.NavigateUrl(alias.Path, pagepath, "guid=" + BlogSubscriber.Guid + "&action=verify"); + body += $"

Verify: {url}"; + var notification = new Notification(alias.SiteId, "", sender, "", BlogSubscriber.Email, "Blog Subscription", body); + _NotificationRepository.AddNotification(notification); } } } + // PUT api//5 + [HttpPut("{moduleid}/{guid}")] + [IgnoreAntiforgeryToken] + public void Put(int moduleid, string guid) + { + BlogSubscriber BlogSubscriber = _BlogSubscriberRepository.GetBlogSubscribers(moduleid).FirstOrDefault(item => item.Guid == guid); + if (BlogSubscriber != null) + { + BlogSubscriber.IsVerified = true; + BlogSubscriber = _BlogSubscriberRepository.UpdateBlogSubscriber(BlogSubscriber); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Blog Subscriber Updated {BlogSubscriber}", BlogSubscriber); + } + } + // DELETE api//5/guid [HttpDelete("{moduleid}/{guid}")] [IgnoreAntiforgeryToken] diff --git a/Server/Migrations/05020101_AddBlogSubscriberIsVerified.cs b/Server/Migrations/05020101_AddBlogSubscriberIsVerified.cs new file mode 100644 index 0000000..9a35a19 --- /dev/null +++ b/Server/Migrations/05020101_AddBlogSubscriberIsVerified.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations; +using Oqtane.Blogs.Migrations.EntityBuilders; +using Oqtane.Blogs.Repository; + +namespace Oqtane.Blogs.Migrations +{ + [DbContext(typeof(BlogContext))] + [Migration("Blog.05.02.01.01")] + public class AddBlogSubscriberIsVerified : MultiDatabaseMigration + { + public AddBlogSubscriberIsVerified(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var blogSubscriberEntityBuilder = new BlogSubscriberEntityBuilder(migrationBuilder, ActiveDatabase); + blogSubscriberEntityBuilder.AddBooleanColumn("IsVerified", true); + blogSubscriberEntityBuilder.UpdateColumn("IsVerified", "1", "bool", ""); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var blogSubscriberEntityBuilder = new BlogSubscriberEntityBuilder(migrationBuilder, ActiveDatabase); + blogSubscriberEntityBuilder.DropColumn("IsVerified"); + } + } +} diff --git a/Server/Repository/BlogSubscriberRepository.cs b/Server/Repository/BlogSubscriberRepository.cs index fd0c71f..227102c 100644 --- a/Server/Repository/BlogSubscriberRepository.cs +++ b/Server/Repository/BlogSubscriberRepository.cs @@ -10,6 +10,7 @@ public interface IBlogSubscriberRepository { IEnumerable GetBlogSubscribers(int ModuleId); BlogSubscriber AddBlogSubscriber(BlogSubscriber BlogSubscriber); + BlogSubscriber UpdateBlogSubscriber(BlogSubscriber BlogSubscriber); void DeleteBlogSubscriber(int SubscriberId); } @@ -40,6 +41,14 @@ public BlogSubscriber AddBlogSubscriber(BlogSubscriber BlogSubscriber) return null; } + public BlogSubscriber UpdateBlogSubscriber(BlogSubscriber BlogSubscriber) + { + using var db = _dbContextFactory.CreateDbContext(); + db.Entry(BlogSubscriber).State = EntityState.Modified; + db.SaveChanges(); + return BlogSubscriber; + } + public void DeleteBlogSubscriber(int BlogSubscriberId) { using var db = _dbContextFactory.CreateDbContext(); diff --git a/Shared/Models/Blog.cs b/Shared/Models/Blog.cs index 18780ef..eff706b 100644 --- a/Shared/Models/Blog.cs +++ b/Shared/Models/Blog.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Security.Cryptography; -using Oqtane.Blogs.Shared; using Oqtane.Models; namespace Oqtane.Blogs.Models diff --git a/Shared/Models/BlogSubscriber.cs b/Shared/Models/BlogSubscriber.cs index f18b2a3..c95a531 100644 --- a/Shared/Models/BlogSubscriber.cs +++ b/Shared/Models/BlogSubscriber.cs @@ -9,6 +9,7 @@ public class BlogSubscriber : IAuditable public int ModuleId { get; set; } public string Email { get; set; } public string Guid { get; set; } + public bool IsVerified { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; }