diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs index 0343a3bc58..763218edc4 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs @@ -167,6 +167,7 @@ public class CmsKitWebUnifiedModule : AbpModule Configure(options => { options.EntityTypes.Add(new CommentEntityTypeDefinition("quote")); + options.IsRecaptchaEnabled = true; }); Configure(options => diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Comments/CommentWithAuthorDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Comments/CommentWithAuthorDto.cs index 18eaa9a4c6..ab833cfb3b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Comments/CommentWithAuthorDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Comments/CommentWithAuthorDto.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using Volo.CmsKit.Users; namespace Volo.CmsKit.Admin.Comments; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CmsKitCommentOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs similarity index 60% rename from modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CmsKitCommentOptions.cs rename to modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs index ca8476073c..94d3b785ec 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CmsKitCommentOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs @@ -7,4 +7,10 @@ public class CmsKitCommentOptions { [NotNull] public List EntityTypes { get; } = new List(); + + /// + /// Flag to enable/disable ReCaptcha for comment component. + /// Default: false + /// + public bool IsRecaptchaEnabled { get; set; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs similarity index 95% rename from modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs rename to modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs index 3dfa822f6a..1cbfc2e215 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs @@ -1,5 +1,4 @@ using JetBrains.Annotations; -using System; using Volo.Abp; namespace Volo.CmsKit.Comments; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/EntityTypeDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/EntityTypeDefinition.cs similarity index 100% rename from modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/EntityTypeDefinition.cs rename to modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/EntityTypeDefinition.cs diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 19e5de70af..cab77cdd26 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -209,6 +209,9 @@ "Feature:ReactionEnable": "Reaction", "Feature:ReactionEnableDescription": "CMS Kit's reaction system that allows users to send reactions to entities such as BlogPost, Comments, etc.", "Feature:TagEnable": "Taging", - "Feature:TagEnableDescription": "CMS Kit's tag system that allows tagging entities such as BlogPost." + "Feature:TagEnableDescription": "CMS Kit's tag system that allows tagging entities such as BlogPost.", + "DeleteBlogPostMessage": "The blog will be deleted. Are you sure?", + "CaptchaCode": "Captcha code", + "CommentTextRequired": "Comment is required" } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index a93e8b3f1c..8b817b5985 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -209,6 +209,9 @@ "Feature:ReactionEnable": "Reaksiyon", "Feature:ReactionEnableDescription": "Cmskit reaksiyon sistemi", "Feature:TagEnable": "Etkiket", - "Feature:TagEnableDescription": "Cmskit etiket sistemi" + "Feature:TagEnableDescription": "Cmskit etiket sistemi", + "DeleteBlogPostMessage": "Blog silinecek. Emin misiniz?", + "CaptchaCode": "Captcha kodu", + "CommentTextRequired": "Yorum zorunlu" } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs index a6462d937c..dd21e2c903 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs @@ -17,4 +17,5 @@ public interface IBlogPostPublicAppService : IApplicationService Task> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input); Task GetAuthorHasBlogPostAsync(Guid id); + Task DeleteAsync(Guid id); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs index 6ae524f0f2..c0eae8650e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs @@ -13,4 +13,8 @@ public class CreateCommentInput public string Text { get; set; } public Guid? RepliedCommentId { get; set; } + + public Guid? CaptchaToken { get; set; } + + public int CaptchaAnswer { get; set; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs new file mode 100644 index 0000000000..7e156ef375 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; +using Volo.CmsKit.Comments; + +namespace Volo.CmsKit.Public.Comments; + +[Serializable] +public class CreateCommentWithParameteresInput +{ + [Required] + [DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.MaxTextLength))] + public string Text { get; set; } + + [Required] + public string EntityType { get; set; } + + [Required] + public string EntityId { get; set; } + + public Guid? RepliedCommentId { get; set; } + + public Guid? CaptchaToken { get; set; } + + public int CaptchaAnswer { get; set; } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj index 9f6d08fea6..c062ee451a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj @@ -1,4 +1,4 @@ - + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs index 5041406bd0..91b7936251 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; +using Volo.Abp.Authorization; using Volo.Abp.Features; using Volo.Abp.GlobalFeatures; +using Volo.Abp.Users; using Volo.CmsKit.Blogs; - using Volo.CmsKit.Contents; using Volo.CmsKit.Features; using Volo.CmsKit.GlobalFeatures; @@ -70,4 +72,17 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub return ObjectMapper.Map(author); } + + [Authorize] + public async Task DeleteAsync(Guid id) + { + var rating = await BlogPostRepository.GetAsync(id); + + if (rating.CreatorId != CurrentUser.GetId()) + { + throw new AbpAuthorizationException(); + } + + await BlogPostRepository.DeleteAsync(id); + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs index ef7e6be9fd..e36cff5717 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs @@ -68,7 +68,6 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli ) ); - await UnitOfWorkManager.Current.SaveChangesAsync(); await DistributedEventBus.PublishAsync(new CreatedCommentEvent diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs index 79b883aa49..9fc813e899 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs @@ -50,4 +50,12 @@ public partial class BlogPostPublicClientProxy : ClientProxyBase> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input) + public virtual Task> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input) { return BlogPostPublicAppService.GetAuthorsHasBlogPostsAsync(input); } [HttpGet] [Route("authors/{id}")] - public Task GetAuthorHasBlogPostAsync(Guid id) + public virtual Task GetAuthorHasBlogPostAsync(Guid id) { return BlogPostPublicAppService.GetAuthorHasBlogPostAsync(id); } + + [HttpDelete] + [Route("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return BlogPostPublicAppService.DeleteAsync(id); + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs index f5884149d4..3c44f6fa1c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs @@ -1,4 +1,6 @@ using AutoMapper; +using Volo.CmsKit.Menus; +using Volo.CmsKit.Public.Comments; namespace Volo.CmsKit.Public.Web; @@ -6,6 +8,6 @@ public class CmsKitPublicWebAutoMapperProfile : Profile { public CmsKitPublicWebAutoMapperProfile() { - + CreateMap(); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs new file mode 100644 index 0000000000..bb2487587b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.ObjectMapping; +using Volo.CmsKit.Comments; +using Volo.CmsKit.Public.Comments; +using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting; +using Volo.CmsKit.Public.Web.Security.Captcha; + +namespace Volo.CmsKit.Public.Web.Controllers; + +//[Route("cms-kit/public-comments")] +public class CmsKitPublicCommentsController : AbpController +{ + public ICommentPublicAppService CommentPublicAppService { get; } + protected CmsKitCommentOptions CmsKitCommentOptions { get; } + public SimpleMathsCaptchaGenerator SimpleMathsCaptchaGenerator { get; } + + public CmsKitPublicCommentsController( + ICommentPublicAppService commentPublicAppService, + IOptions cmsKitCommentOptions, + SimpleMathsCaptchaGenerator simpleMathsCaptchaGenerator) + { + CommentPublicAppService = commentPublicAppService; + CmsKitCommentOptions = cmsKitCommentOptions.Value; + SimpleMathsCaptchaGenerator = simpleMathsCaptchaGenerator; + } + + [HttpPost] + public async Task ValidateAsync([FromBody] CreateCommentWithParameteresInput input) + { + if (CmsKitCommentOptions.IsRecaptchaEnabled && input.CaptchaToken.HasValue) + { + SimpleMathsCaptchaGenerator.Validate(input.CaptchaToken.Value, input.CaptchaAnswer); + } + + var dto = ObjectMapper.Map (input); + await CommentPublicAppService.CreateAsync(input.EntityType, input.EntityId, dto); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs index 81fd94406d..bdf8d3ffa0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -7,7 +8,9 @@ using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.CmsKit.Comments; using Volo.CmsKit.Public.Comments; +using Volo.CmsKit.Public.Web.Security.Captcha; using Volo.CmsKit.Web.Renderers; namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting; @@ -24,15 +27,34 @@ public class CommentingViewComponent : AbpViewComponent public ICommentPublicAppService CommentPublicAppService { get; } public IMarkdownToHtmlRenderer MarkdownToHtmlRenderer { get; } public AbpMvcUiOptions AbpMvcUiOptions { get; } + public CmsKitCommentOptions CmsKitCommentOptions { get; } + public SimpleMathsCaptchaGenerator SimpleMathsCaptchaGenerator { get; } + + [HiddenInput] + [BindProperty] + public string RecaptchaToken { get; set; } + + [HiddenInput] + [BindProperty] + public Guid CaptchaId { get; set; } + + [BindProperty] + public CommentingViewModel Input { get; set; } + + public CaptchaOutput CaptchaOutput { get; set; } public CommentingViewComponent( ICommentPublicAppService commentPublicAppService, IOptions options, - IMarkdownToHtmlRenderer markdownToHtmlRenderer) + IMarkdownToHtmlRenderer markdownToHtmlRenderer, + IOptions cmsKitCommentOptions, + SimpleMathsCaptchaGenerator simpleMathsCaptchaGenerator) { CommentPublicAppService = commentPublicAppService; MarkdownToHtmlRenderer = markdownToHtmlRenderer; AbpMvcUiOptions = options.Value; + CmsKitCommentOptions = cmsKitCommentOptions.Value; + SimpleMathsCaptchaGenerator = simpleMathsCaptchaGenerator; } public virtual async Task InvokeAsync( @@ -42,7 +64,6 @@ public class CommentingViewComponent : AbpViewComponent var comments = (await CommentPublicAppService .GetListAsync(entityType, entityId)).Items; - var loginUrl = $"{AbpMvcUiOptions.LoginUrl}?returnUrl={HttpContext.Request.Path.ToString()}&returnUrlHash=#cms-comment_{entityType}_{entityId}"; var viewModel = new CommentingViewModel @@ -52,10 +73,26 @@ public class CommentingViewComponent : AbpViewComponent LoginUrl = loginUrl, Comments = comments.OrderByDescending(i => i.CreationTime).ToList() }; - await ConvertMarkdownTextsToHtml(viewModel); - return View("~/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml", viewModel); + if (CmsKitCommentOptions.IsRecaptchaEnabled) + { + CaptchaOutput = SimpleMathsCaptchaGenerator.Generate(new CaptchaOptions( + number1MinValue: 1, + number1MaxValue: 10, + number2MinValue: 5, + number2MaxValue: 15) + ); + + viewModel.CaptchaImageBase64 = GetCaptchaImageBase64(CaptchaOutput.ImageBytes); + } + this.Input = viewModel; + return View("~/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml", this); + } + + private string GetCaptchaImageBase64(byte[] bytes) + { + return $"data:image/jpg;base64,{Convert.ToBase64String(bytes)}"; } private async Task ConvertMarkdownTextsToHtml(CommentingViewModel viewModel) @@ -86,6 +123,12 @@ public class CommentingViewComponent : AbpViewComponent public IReadOnlyList Comments { get; set; } public Dictionary RawCommentTexts { get; set; } + + [Required] + [StringLength(100, MinimumLength = 1)] + public string Captcha { get; set; } + + public string CaptchaImageBase64 { get; set; } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml index 9d0438098d..ecfbc1cb54 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml @@ -3,6 +3,7 @@ @using Microsoft.Extensions.Options @using Volo.Abp.GlobalFeatures @using Volo.Abp.Users +@using Volo.CmsKit.Comments; @using Volo.CmsKit.GlobalFeatures @using Volo.CmsKit.Localization @using Volo.CmsKit.Public.Comments @@ -10,8 +11,9 @@ @using Volo.CmsKit.Web @inject ICurrentUser CurrentUser @inject IOptionsSnapshot cmsKitUiOptions; +@inject IOptions CmsKitCommentOptions @inject IHtmlLocalizer L -@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting.CommentingViewComponent.CommentingViewModel +@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting.CommentingViewComponent @{ Func GetCommentTitle(CmsUserDto author, DateTime creationTime) => @@ -36,9 +38,29 @@ +
+ @L["MarkdownSupported"] +
+ + @if (CmsKitCommentOptions.Value.IsRecaptchaEnabled) + { +
+ +
+
+ +
+
+ +
+ + +
+
+ }
- + @L["Send"] @if (cancelButton) @@ -49,9 +71,6 @@ }
-
- @L["MarkdownSupported"] -
; @@ -71,13 +90,13 @@ { @if (CurrentUser.IsAuthenticated) { - + @L["Reply"] } else { - @L["LoginToReply"] + @L["LoginToReply"] } } @if (authorId == CurrentUser.Id) @@ -93,9 +112,10 @@ } @{ Func GetEditArea(Guid id, string text, string concurrencyStamp) => - @ } + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css index 469c8274f6..8d241afcd5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css @@ -3,4 +3,28 @@ } .comment-links:hover { text-decoration: none; -} \ No newline at end of file +} +body .volo-captcha img { + border: 1px outset #dddcdc; + border-radius: 4px; + height: 45px; +} + +body .volo-captcha .d-flex .form-group { + width: 100%; +} + +@media (max-width: 800px) { + body .volo-captcha #Input_Captcha { + margin-left: 0px; + } +} + +body .form-control { + border: 1px outset #e3e3e3 !important; + padding: 10px !important; +} + +body #submit-button { + margin-top: 10px; +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js index a4fca33947..e0fcca9ad1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js @@ -1,10 +1,10 @@ (function ($) { - var l = abp.localization.getResource('CmsKit'); + let l = abp.localization.getResource('CmsKit'); abp.widgets.CmsCommenting = function ($widget) { - var widgetManager = $widget.data('abp-widget-manager'); - var $commentArea = $widget.find('.cms-comment-area'); + let widgetManager = $widget.data('abp-widget-manager'); + let $commentArea = $widget.find('.cms-comment-area'); function getFilters() { return { @@ -15,14 +15,14 @@ function registerEditLinks($container) { $container.find('.comment-edit-link').each(function () { - var $link = $(this); + let $link = $(this); $link.on('click', function (e) { e.preventDefault(); - var commentId = $link.data('id'); + let commentId = $link.data('id'); - var $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id=' + commentId + ']'); - var $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id=' + commentId + ']'); + let $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id=' + commentId + ']'); + let $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id=' + commentId + ']'); $relatedCommentContentArea.hide(); $relatedCommentEditFormArea.show(); @@ -30,15 +30,15 @@ }); }); $container.find('.comment-edit-cancel-button').each(function () { - var $button = $(this); + let $button = $(this); $button.on('click', function (e) { e.preventDefault(); - var commentId = $button.data('id'); + let commentId = $button.data('id'); - var $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id=' + commentId + ']'); - var $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id=' + commentId + ']'); - var $link = $container.find('.comment-edit-link[data-id=' + commentId + ']'); + let $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id=' + commentId + ']'); + let $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id=' + commentId + ']'); + let $link = $container.find('.comment-edit-link[data-id=' + commentId + ']'); $relatedCommentContentArea.show(); $relatedCommentEditFormArea.hide(); @@ -49,14 +49,14 @@ function registerReplyLinks($container) { $container.find('.comment-reply-link').each(function () { - var $link = $(this); + let $link = $(this); $link.on('click', function (e) { e.preventDefault(); - var replyCommentId = $link.data('reply-id'); + let replyCommentId = $link.data('reply-id'); - var $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id=' + replyCommentId + ']'); - var $links = $container.find('.comment-reply-link[data-reply-id=' + replyCommentId + ']'); + let $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id=' + replyCommentId + ']'); + let $links = $container.find('.comment-reply-link[data-reply-id=' + replyCommentId + ']'); $relatedCommentArea.show(); $relatedCommentArea.find('textarea').focus(); @@ -64,14 +64,14 @@ }); }); $container.find('.reply-cancel-button').each(function () { - var $button = $(this); + let $button = $(this); $button.on('click', function (e) { e.preventDefault(); - var replyCommentId = $button.data('reply-id'); + let replyCommentId = $button.data('reply-id'); - var $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id=' + replyCommentId + ']'); - var $links = $container.find('.comment-reply-link[data-reply-id=' + replyCommentId + ']'); + let $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id=' + replyCommentId + ']'); + let $links = $container.find('.comment-reply-link[data-reply-id=' + replyCommentId + ']'); $relatedCommentArea.hide(); $links.removeClass('disabled'); @@ -81,7 +81,7 @@ function registerDeleteLinks($container) { $container.find('.comment-delete-link').each(function () { - var $link = $(this); + let $link = $(this); $link.on('click', '', function (e) { e.preventDefault(); @@ -99,10 +99,10 @@ function registerUpdateOfNewComment($container) { $container.find('.cms-comment-update-form').each(function () { - var $form = $(this); + let $form = $(this); $form.submit(function (e) { e.preventDefault(); - var formAsObject = $form.serializeFormToObject(); + let formAsObject = $form.serializeFormToObject(); volo.cmsKit.public.comments.commentPublic.update( formAsObject.id, { @@ -118,24 +118,39 @@ function registerSubmissionOfNewComment($container) { $container.find('.cms-comment-form').each(function () { - var $form = $(this); + let $form = $(this); $form.submit(function (e) { e.preventDefault(); - var formAsObject = $form.serializeFormToObject(); + let formAsObject = $form.serializeFormToObject(); - if (formAsObject.repliedCommentId == ''){ + if (formAsObject.repliedCommentId == '') { formAsObject.repliedCommentId = null; } - volo.cmsKit.public.comments.commentPublic.create( - $commentArea.attr('data-entity-type'), - $commentArea.attr('data-entity-id'), - { + if (formAsObject.commentText == '') { + abp.message.error(l("CommentTextRequired")); + return; + } + + $.ajax({ + type: 'POST', + url: '/CmsKitPublicComments/Validate', + contentType: 'application/json; charset=utf-8', + dataType: 'json', + data: JSON.stringify({ + entityId: $commentArea.attr('data-entity-id'), + entityType: $commentArea.attr('data-entity-type'), repliedCommentId: formAsObject.repliedCommentId, - text: formAsObject.commentText + text: formAsObject.commentText, + captchaToken: formAsObject.captchaId, + captchaAnswer: formAsObject.input?.captcha + }), + success: function () { + widgetManager.refresh($widget); + }, + error: function (data) { + abp.message.error(data.responseJSON.error.message); } - ).then(function () { - widgetManager.refresh($widget); }); }); }); @@ -146,7 +161,7 @@ return; } - var $link = $(location.hash + '_link'); + let $link = $(location.hash + '_link'); if ($link.length > 0) { $link.click(); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.scss b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.scss new file mode 100644 index 0000000000..82d41f9795 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.scss @@ -0,0 +1,30 @@ +body { + .volo-captcha { + img { + border: 1px outset #dddcdc; + border-radius: 4px; + height: 45px; + } + + .d-flex { + .form-group { + width: 100%; + } + } + + @media (max-width: 800px) { + #Input_Captcha { + margin-left: 0px; + } + } + } + + .form-control { + border: 1px outset #e3e3e3 !important; + padding: 10px !important; + } + + #submit-button { + margin-top: 10px; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml index d5dc5a0be8..485aa20ac3 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml @@ -1,6 +1,7 @@ @page @using Volo.Abp.Data +@using Volo.Abp.Users; @using Volo.CmsKit.Public.Blogs @using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment @using Volo.CmsKit.Public.Web.Pages @@ -17,9 +18,9 @@ @model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs.BlogPostModel +@inject ICurrentUser CurrentUser @inject IMarkdownToHtmlRenderer MarkdownRenderer - @{ string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.ViewModel.Title; var isScrollIndexEnabled = GlobalFeatureManager.Instance.IsEnabled() && Model.BlogPostScrollIndexFeature?.IsEnabled == true; @@ -50,14 +51,15 @@ + } -
+
@@ -109,10 +111,10 @@ if (Model.ReactionsFeature?.IsEnabled == true) { @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new - { - entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, - entityId = Model.ViewModel.Id.ToString() - }) + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.ViewModel.Id.ToString() + }) } } @@ -122,10 +124,10 @@ if (Model.RatingsFeature?.IsEnabled == true) { @await Component.InvokeAsync(typeof(RatingViewComponent), new - { - entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, - entityId = Model.ViewModel.Id.ToString() - }) + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.ViewModel.Id.ToString() + }) } } @@ -133,24 +135,33 @@
- @if (isScrollIndexEnabled) - { -
-
-
@L["InThisDocument"]
- -
-
- - @L["GoToTop"] - +
+ @if (Model.ViewModel.Author.Id == CurrentUser.Id) + { + + } + @if (isScrollIndexEnabled) + { +
+
@L["InThisDocument"]
+ + +
-
-
- } + } +
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs index 87630f9bd0..a503ff0794 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.GlobalFeatures; -using Volo.Abp.ObjectMapping; using Volo.CmsKit.Blogs; using Volo.CmsKit.Contents; using Volo.CmsKit.GlobalFeatures; diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.js new file mode 100644 index 0000000000..160cfd3b65 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.js @@ -0,0 +1,16 @@ +$(function () { + + let l = abp.localization.getResource("CmsKit"); + + $('#deleteBlogPost').on('click', '', function (e) { + abp.message.confirm(l("DeleteBlogPostMessage"), function (ok) { + if (ok) { + volo.cmsKit.public.blogs.blogPostPublic.delete( + $('#BlogId').val() + ).then(function () { + document.location.href = "/"; + }); + } + }) + }); +}); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.js index 0d567d1650..dde3ed4e89 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.js @@ -1,14 +1,14 @@ $(function () { - var $selectAuthor = $('#AuthorSelect'); - var $authorNameSpan = $('.author-name-span'); + let $selectAuthor = $('#AuthorSelect'); + let $authorNameSpan = $('.author-name-span'); $selectAuthor.on('change', function () { - var authorId = $selectAuthor.val(); + let authorId = $selectAuthor.val(); reloadPageWithQueryString({'authorId': authorId}); }); $authorNameSpan.click(function () { - var authorId = $(this).data('author-id'); + let authorId = $(this).data('author-id'); reloadPageWithQueryString({'authorId': authorId}); }); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaException.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaException.cs new file mode 100644 index 0000000000..7914e85b02 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaException.cs @@ -0,0 +1,15 @@ +using System.Runtime.Serialization; +using Volo.Abp; + +namespace Volo.CmsKit.Public.Web.Security.Captcha; + +public class CaptchaException : UserFriendlyException +{ + public CaptchaException(string message) : base(message) + { + } + + public CaptchaException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context) + { + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOptions.cs new file mode 100644 index 0000000000..3db5aa06b6 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOptions.cs @@ -0,0 +1,66 @@ +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; + +namespace Volo.CmsKit.Public.Web.Security.Captcha; + +public class CaptchaOptions +{ + /// + /// Default fonts are "Arial", "Verdana", "Times New Roman" in Windows. These fonts must exist in the target OS. + /// + public string[] FontFamilies { get; set; } = new string[] { "Arial", "Verdana", "Times New Roman" }; + + public Color[] TextColor { get; set; } = new Color[] + { + Color.Blue, Color.Black, Color.Black, Color.Brown, Color.Gray, Color.Green + }; + public Color[] DrawLinesColor { get; set; } = new Color[] + { + Color.Blue, Color.Black, Color.Black, Color.Brown, Color.Gray, Color.Green + }; + + public float MinLineThickness { get; set; } = 0.7f; + + public float MaxLineThickness { get; set; } = 2.0f; + + public ushort Width { get; set; } = 180; + + public ushort Height { get; set; } = 70; + + public ushort NoiseRate { get; set; } = 500; + + public Color[] NoiseRateColor { get; set; } = new Color[] { Color.Gray }; + + public byte FontSize { get; set; } = 32; + + public FontStyle FontStyle { get; set; } = FontStyle.Regular; + + public EncoderTypes EncoderType { get; set; } = EncoderTypes.Png; + + public IImageEncoder Encoder => RandomTextGenerator.GetEncoder(EncoderType); + + public byte DrawLines { get; set; } = 2; + + public byte MaxRotationDegrees { get; set; } = 4; + + public int Number1MinValue { get; set; } = 1; + + public int Number1MaxValue { get; set; } = 99; + + public int Number2MinValue { get; set; } = 1; + + public int Number2MaxValue { get; set; } = 99; + + public CaptchaOptions() + { + + } + public CaptchaOptions(int number1MinValue, int number1MaxValue, int number2MinValue, int number2MaxValue) + { + Number1MinValue = number1MinValue; + Number1MaxValue = number1MaxValue; + Number2MinValue = number2MinValue; + Number1MaxValue = number2MaxValue; + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOutput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOutput.cs new file mode 100644 index 0000000000..3a483aa46a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/CaptchaOutput.cs @@ -0,0 +1,32 @@ +using System; + +namespace Volo.CmsKit.Public.Web.Security.Captcha; + +public class CaptchaOutput +{ + public Guid Id { get; set; } + public string Text { get; set; } + public byte[] ImageBytes { get; set; } + public int Result { get; set; } +} + +public class CaptchaInput +{ + public int Number1 { get; set; } + public int Number2 { get; set; } +} + +public class CaptchaRequest +{ + public CaptchaInput Input { get; set; } + public CaptchaOutput Output { get; set; } + + public CaptchaRequest() + { + Input = new CaptchaInput(); + Output = new CaptchaOutput + { + Id = Guid.NewGuid() + }; + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/EncoderTypes.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/EncoderTypes.cs new file mode 100644 index 0000000000..60308946f0 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/EncoderTypes.cs @@ -0,0 +1,7 @@ +namespace Volo.CmsKit.Public.Web.Security.Captcha; + +public enum EncoderTypes +{ + Jpeg, + Png, +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/RandomTextGenerator.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/RandomTextGenerator.cs new file mode 100644 index 0000000000..df5818f88c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/RandomTextGenerator.cs @@ -0,0 +1,73 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; + +namespace Volo.CmsKit.Public.Web.Security.Captcha; +public static class RandomTextGenerator +{ + private static readonly char[] AllowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVXYZW23456789".ToCharArray(); + + public static IImageEncoder GetEncoder(EncoderTypes encoderType) + { + IImageEncoder encoder = encoderType switch + { + EncoderTypes.Png => new PngEncoder(), + EncoderTypes.Jpeg => new JpegEncoder(), + _ => throw new ArgumentException($"Encoder '{encoderType}' not found!") + }; + + return encoder; + } + + public static string GetRandomText(int size) + { + var data = new byte[4 * size]; + using (var crypto = new RNGCryptoServiceProvider()) + { + crypto.GetBytes(data); + } + + var result = new StringBuilder(size); + for (var i = 0; i < size; i++) + { + var rnd = BitConverter.ToUInt32(data, i * 4); + var idx = rnd % AllowedChars.Length; + result.Append(AllowedChars[idx]); + } + + return result.ToString(); + } + + public static string GetUniqueKey(int size, char[] chars) + { + var data = new byte[4 * size]; + using (var crypto = new RNGCryptoServiceProvider()) + { + crypto.GetBytes(data); + } + + var result = new StringBuilder(size); + + for (var i = 0; i < size; i++) + { + var rnd = BitConverter.ToUInt32(data, i * 4); + var idx = rnd % chars.Length; + result.Append(chars[idx]); + } + + return result.ToString(); + } + + public static float GenerateNextFloat(double min = -3.40282347E+38, double max = 3.40282347E+38) + { + var random = new Random(); + var range = max - min; + var sample = random.NextDouble(); + var scaled = sample * range + min; + var result = (float)scaled; + return result; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/SimpleMathsCaptchaGenerator.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/SimpleMathsCaptchaGenerator.cs new file mode 100644 index 0000000000..b9268a7f6a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Security/Captcha/SimpleMathsCaptchaGenerator.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Volo.Abp.DependencyInjection; +using Color = SixLabors.ImageSharp.Color; +using PointF = SixLabors.ImageSharp.PointF; + +namespace Volo.CmsKit.Public.Web.Security.Captcha; + +public class SimpleMathsCaptchaGenerator : ISingletonDependency +{ + private static Dictionary Session { get; set; } = new Dictionary(); + + public CaptchaOutput Generate() + { + return Generate(options: null, number1: null, number2: null); + } + + public CaptchaOutput Generate(CaptchaOptions options) + { + return Generate(options, number1: null, number2: null); + } + + /// + /// Creates a simple captcha code. + /// + /// Options for captcha generation + /// First number for maths operation + /// Second number for maths operation + /// + public CaptchaOutput Generate(CaptchaOptions options, int? number1, int? number2) + { + var random = new Random(); + options ??= new CaptchaOptions(); + + number1 ??= random.Next(options.Number1MinValue, options.Number1MaxValue); + number2 ??= random.Next(options.Number2MinValue, options.Number2MaxValue); + + var text = number1 + "+" + number2; + var request = new CaptchaRequest + { + Input = + { + Number1 = number1.Value, + Number2 = number2.Value + }, + Output = + { + Text = text, + Result = Calculate(number1.Value, number2.Value), + ImageBytes = GenerateInternal(text, options) + } + }; + + Session[request.Output.Id] = request; + return request.Output; + } + + private static int Calculate(int number1, int number2) + { + return number1 + number2; + } + + public void Validate(Guid requestId, int value) + { + var request = Session[requestId]; + if (request.Output.Result != value) + { + throw new CaptchaException("The captcha code doesn't match text on the picture! Please try again."); + } + } + + public void Validate(Guid requestId, string value) + { + if (int.TryParse(value, out var captchaInput)) + { + Validate(requestId, captchaInput); + } + else + { + throw new CaptchaException("The captcha code is missing!"); + } + } + + private byte[] GenerateInternal(string stringText, CaptchaOptions options) + { + byte[] result; + + using (var image = new Image(options.Width, options.Height)) + { + float position = 0; + var random = new Random(); + var startWith = (byte)random.Next(5, 10); + image.Mutate(ctx => ctx.BackgroundColor(Color.Transparent)); + var fontName = options.FontFamilies[random.Next(0, options.FontFamilies.Length)]; + var font = SystemFonts.CreateFont(fontName, options.FontSize, options.FontStyle); + + foreach (var character in stringText) + { + var text = character.ToString(); + var color = options.TextColor[random.Next(0, options.TextColor.Length)]; + var location = new PointF(startWith + position, random.Next(6, 13)); + image.Mutate(ctx => ctx.DrawText(text, font, color, location)); + position += TextMeasurer.Measure(character.ToString(), new RendererOptions(font, location)).Width; + } + + //add rotation + var rotation = GetRotation(options); + image.Mutate(ctx => ctx.Transform(rotation)); + + // add the dynamic image to original image + var size = (ushort)TextMeasurer.Measure(stringText, new RendererOptions(font)).Width; + var img = new Image(size + 15, options.Height); + img.Mutate(ctx => ctx.BackgroundColor(Color.White)); + + Parallel.For(0, options.DrawLines, i => + { + var x0 = random.Next(0, random.Next(0, 30)); + var y0 = random.Next(10, img.Height); + + var x1 = random.Next(30, img.Width); + var y1 = random.Next(0, img.Height); + + img.Mutate(ctx => + ctx.DrawLines(options.TextColor[random.Next(0, options.TextColor.Length)], + RandomTextGenerator.GenerateNextFloat(options.MinLineThickness, options.MaxLineThickness), + new PointF[] { new PointF(x0, y0), new PointF(x1, y1) }) + ); + }); + + img.Mutate(ctx => ctx.DrawImage(image, 0.80f)); + + Parallel.For(0, options.NoiseRate, i => + { + var x0 = random.Next(0, img.Width); + var y0 = random.Next(0, img.Height); + img.Mutate( + ctx => ctx + .DrawLines(options.NoiseRateColor[random.Next(0, options.NoiseRateColor.Length)], + RandomTextGenerator.GenerateNextFloat(0.5, 1.5), new PointF[] { new Vector2(x0, y0), new Vector2(x0, y0) }) + ); + }); + + img.Mutate(x => + { + x.Resize(options.Width, options.Height); + }); + + using (var ms = new MemoryStream()) + { + img.Save(ms, options.Encoder); + result = ms.ToArray(); + } + } + + return result; + } + + private static AffineTransformBuilder GetRotation(CaptchaOptions options) + { + var random = new Random(); + var width = random.Next(10, options.Width); + var height = random.Next(10, options.Height); + var pointF = new PointF(width, height); + var rotationDegrees = random.Next(0, options.MaxRotationDegrees); + return new AffineTransformBuilder().PrependRotationDegrees(rotationDegrees, pointF); + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj index da794f2eac..7aaa2eda5e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj @@ -14,10 +14,13 @@ - + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js index ec98417af5..2d2be254a6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js @@ -214,6 +214,14 @@ }, ajaxParams)); }; + volo.cmsKit.public.blogs.blogPostPublic['delete'] = function(id, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/cms-kit-public/blog-posts/' + id + '', + type: 'DELETE', + dataType: null + }, ajaxParams)); + }; + })(); })();