Browse Source

Merge pull request #13082 from abpframework/cmskit-widget-render

Cmskit widget render
pull/13093/head
Enis Necipoglu 4 years ago
committed by GitHub
parent
commit
180b854231
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpDictionaryExtensions.cs
  2. 2
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/appsettings.json
  3. 8
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentConsts.cs
  4. 10
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentFragment.cs
  5. 18
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/CmsKitContentWidgetOptions.cs
  6. 120
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentParser.cs
  7. 11
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentWidgetConfig.cs
  8. 4
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs
  9. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs
  10. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/PageDto.cs
  11. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  12. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs
  13. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs
  14. 63
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  15. 17
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml
  16. 54
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/DefaultPageViewComponent.cs
  17. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs
  18. 115
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  19. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs
  20. 41
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
  21. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs
  22. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs
  23. 138
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs
  24. 13
      modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Pages/PageManager_Test.cs
  25. 4
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs

19
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpDictionaryExtensions.cs

@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Dynamic;
namespace System.Collections.Generic;
@ -128,4 +129,22 @@ public static class AbpDictionaryExtensions
{
return dictionary.GetOrAdd(key, k => factory());
}
/// <summary>
/// Converts a <string,object> dictionary to dynamic object so added and removed at run
/// </summary>
/// <param name="dictionary">The collection object</param>
/// <returns>If value is correct, return ExpandoObject that represents an object</returns>
public static dynamic ConvertToDynamicObject(this Dictionary<string, object> dictionary)
{
var expandoObject = new ExpandoObject();
var expendObjectCollection = (ICollection<KeyValuePair<string, object>>)expandoObject;
foreach (var keyValuePair in dictionary)
{
expendObjectCollection.Add(keyValuePair);
}
return expandoObject;
}
}

2
modules/cms-kit/host/Volo.CmsKit.Web.Unified/appsettings.json

@ -1,5 +1,5 @@
{
"ConnectionStrings": {
"Default": "Server=(localdb)\\.\\MSSQLLocalDB;Database=CmsKit_Unified;Trusted_Connection=True"
"Default": "Server=(localdb)\\MSSQLLocalDB;Database=CmsKit_Unified;Trusted_Connection=True"
}
}

8
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentConsts.cs

@ -0,0 +1,8 @@
namespace Volo.CmsKit.Contents;
public static class ContentConsts
{
public static string Delimeter { get; set; } = "----";
public static string Widget { get; set; } = "Widget";
public static string Markdown { get; set; } = "Markdown";
public static string Content { get; set; } = "Content";
}

10
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentFragment.cs

@ -0,0 +1,10 @@
using System;
using Volo.Abp.ObjectExtending;
namespace Volo.CmsKit.Contents;
[Serializable]
public class ContentFragment : ExtensibleObject
{
public string Type { get; set; }
}

18
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/CmsKitContentWidgetOptions.cs

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace Volo.CmsKit.Contents;
public class CmsKitContentWidgetOptions
{
public Dictionary<string, ContentWidgetConfig> WidgetConfigs { get; }
public CmsKitContentWidgetOptions()
{
WidgetConfigs = new();
}
public void AddWidget(string widgetName, string widgetKey)
{
WidgetConfigs.Add(widgetName, new ContentWidgetConfig(widgetKey));
}
}

120
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentParser.cs

@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using static Volo.CmsKit.Contents.ContentConsts;
namespace Volo.CmsKit.Contents;
public class ContentParser : ITransientDependency
{
private readonly CmsKitContentWidgetOptions _options;
public ContentParser(IOptions<CmsKitContentWidgetOptions> options)
{
_options = options.Value;
}
public Task<List<ContentFragment>> ParseAsync(string content)
{
if (!_options.WidgetConfigs.Any())
{
return Task.FromResult(new List<ContentFragment>
{
new ContentFragment { Type = Markdown }.SetProperty(Content, content),
});
}
var parsedList = new List<string>();
ParseContent(content, parsedList);
var contentFragments = new List<ContentFragment>();
FillContentFragment(content, parsedList, contentFragments);
return Task.FromResult(contentFragments);
}
private void ParseContent(string content, List<string> parsedList)
{
var replacedText = Regex.Replace(content, @"\[.*?\]", Delimeter);
if (!replacedText.Contains(Delimeter))
{
parsedList.Add(replacedText);
}
while (replacedText.Contains(Delimeter))
{
//For parsing delimeter
var index = replacedText.IndexOf(Delimeter);
if (index != 0)
{
parsedList.Add(replacedText.Substring(0, index));
replacedText = replacedText.Substring(index, replacedText.Length - index);
index = 0;
}
parsedList.Add(replacedText.Substring(index, Delimeter.Length));
replacedText = replacedText.Substring(Delimeter.Length, replacedText.Length - Delimeter.Length);
//for parsing the other side
index = replacedText.IndexOf(Delimeter);
if (index != -1)
{
parsedList.Add(replacedText.Substring(0, index));
replacedText = replacedText.Substring(index, replacedText.Length - index);
}
else if (replacedText.Length > 0)
{
parsedList.Add(replacedText);
}
}
}
private void FillContentFragment(string content, List<string> parsedList, List<ContentFragment> contentFragments)
{
content = Regex.Replace(content, @"=\s*""", @"=""");
content = Regex.Replace(content, @"""\s*=", @"""=");
var widgets = Regex.Matches(content, @"(?<=\[Widget)(.*?)(?=\])").Cast<Match>().Select(p => p.Value).ToList();
for (int i = 0, k = 0; i < parsedList.Count; i++)
{
if (parsedList[i] == Delimeter)
{
if (widgets.Count > k)
{
var preparedContent = string.Join("", widgets[k]);
var keys = Regex.Matches(preparedContent, @"(?<=\s)(.*?)(?==\s*"")").Cast<Match>()
.Select(p => p.Value).Where(p => p != string.Empty).ToList();
var values = Regex.Matches(preparedContent, @"(?<=\s*[a-zA-Z]*=\s*"")(.*?)(?="")").Cast<Match>()
.Select(p => p.Value).ToList();
var widgetTypeIndex = keys.IndexOf("Type");
if (widgetTypeIndex != -1)
{
var widgetType = values[widgetTypeIndex];
var name = _options.WidgetConfigs.Where(p => p.Key == widgetType).Select(p => p.Value.Name).FirstOrDefault();
if (name is not null && widgets.Count > k)
{
values[0] = name;
var contentFragment = new ContentFragment() { Type = Widget };
contentFragments.Add(contentFragment);
for (int kv = 0; kv < values.Count; kv++)
{
contentFragments.FindLast(p => p == contentFragment)
.SetProperty(keys[kv], values[kv]);
}
}
}
}
k++;
}
else
{
contentFragments.Add(new ContentFragment() { Type = Markdown }
.SetProperty(Content, parsedList[i]));
}
}
}
}

11
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentWidgetConfig.cs

@ -0,0 +1,11 @@
namespace Volo.CmsKit.Contents;
public class ContentWidgetConfig
{
public string Name { get; }
public ContentWidgetConfig(string widgetName)
{
Name = widgetName;
}
}

4
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs

@ -1,6 +1,4 @@
using Volo.CmsKit.GlobalFeatures;
namespace Volo.CmsKit.Blogs;
namespace Volo.CmsKit.Blogs;
public static class BlogPostConsts
{

4
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Public.Blogs;
@ -15,6 +17,8 @@ public class BlogPostPublicDto : AuditedEntityDto<Guid>
public string ShortDescription { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public string Content { get; set; }
public Guid? CoverImageMediaId { get; set; }

4
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/PageDto.cs

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Public.Pages;
@ -11,6 +13,8 @@ public class PageDto : EntityDto<Guid>
public string Slug { get; set; }
public string Content { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
public string Script { get; set; }

14
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Contents;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Users;
@ -16,22 +17,29 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
protected IBlogRepository BlogRepository { get; }
protected IBlogPostRepository BlogPostRepository { get; }
protected ContentParser ContentParser { get; }
public BlogPostPublicAppService(
IBlogRepository blogRepository,
IBlogPostRepository blogPostRepository)
IBlogPostRepository blogPostRepository,
ContentParser contentParser)
{
BlogRepository = blogRepository;
BlogPostRepository = blogPostRepository;
ContentParser = contentParser;
}
public virtual async Task<BlogPostPublicDto> GetAsync([NotNull] string blogSlug, [NotNull] string blogPostSlug)
public virtual async Task<BlogPostPublicDto> GetAsync(
[NotNull] string blogSlug, [NotNull] string blogPostSlug)
{
var blog = await BlogRepository.GetBySlugAsync(blogSlug);
var blogPost = await BlogPostRepository.GetBySlugAsync(blog.Id, blogPostSlug);
return ObjectMapper.Map<BlogPost, BlogPostPublicDto>(blogPost);
var blogPostDto = ObjectMapper.Map<BlogPost, BlogPostPublicDto>(blogPost);
blogPostDto.ContentFragments = await ContentParser.ParseAsync(blogPost.Content);
return blogPostDto;
}
public virtual async Task<PagedResultDto<BlogPostPublicDto>> GetListAsync([NotNull] string blogSlug, BlogPostGetListInput input)

9
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs

@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Contents;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Pages;
@ -9,10 +10,12 @@ namespace Volo.CmsKit.Public.Pages;
public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppService
{
protected IPageRepository PageRepository { get; }
protected ContentParser ContentParser { get; }
public PagePublicAppService(IPageRepository pageRepository)
public PagePublicAppService(IPageRepository pageRepository, ContentParser contentParser)
{
PageRepository = pageRepository;
ContentParser = contentParser;
}
public virtual async Task<PageDto> FindBySlugAsync(string slug)
@ -24,6 +27,8 @@ public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppSe
return null;
}
return ObjectMapper.Map<Page, PageDto>(page);
var pageDto = ObjectMapper.Map<Page, PageDto>(page);
pageDto.ContentFragments = await ContentParser.ParseAsync(page.Content);
return pageDto;
}
}

6
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs

@ -30,9 +30,11 @@ public class PublicApplicationAutoMapperProfile : Profile
CreateMap<Rating, RatingDto>();
CreateMap<Page, PageDto>();
CreateMap<Page, PageDto>()
.Ignore(x => x.ContentFragments);
CreateMap<BlogPost, BlogPostPublicDto>(MemberList.None);
CreateMap<BlogPost, BlogPostPublicDto>()
.Ignore(x => x.ContentFragments);
CreateMap<MenuItem, MenuItemDto>();

63
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json

@ -505,6 +505,57 @@
}
}
},
"Volo.CmsKit.Public.Polls.PollViewComponentController": {
"controllerName": "PollViewComponent",
"controllerGroupName": "PollViewComponent",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Polls.PollViewComponentController",
"interfaces": [
{
"type": "Volo.CmsKit.Public.Polls.IPollViewComponentAppService"
}
],
"actions": {
"ParseAsyncByContent": {
"uniqueName": "ParseAsyncByContent",
"name": "ParseAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/polls/{content}",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "content",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "content",
"name": "content",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "System.Collections.Generic.List<Volo.CmsKit.Polls.ContentFragment>",
"typeSimple": "[Volo.CmsKit.Polls.ContentFragment]"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Polls.IPollViewComponentAppService"
}
}
},
"Volo.CmsKit.Public.Pages.PagesPublicController": {
"controllerName": "PagesPublic",
"controllerGroupName": "PagesPublic",
@ -989,6 +1040,18 @@
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "TagId",
"jsonName": null,
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Sorting",

17
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml

@ -1,17 +0,0 @@
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@using Volo.CmsKit.Public.Web.Renderers
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs
@inject IMarkdownToHtmlRenderer MarkdownRenderer
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel
<abp-card>
<abp-card-body>
@if (!Model.Content.IsNullOrEmpty())
{
@Html.Raw(await MarkdownRenderer.RenderAsync(Model.Content))
}
</abp-card-body>
</abp-card>

54
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/DefaultPageViewComponent.cs

@ -1,54 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.CmsKit.Public.Pages;
using Volo.CmsKit.Public.Web.Renderers;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages;
[Widget(
StyleTypes = new[]
{
typeof(HighlightJsStyleContributor)
},
ScriptTypes = new[]
{
typeof(HighlightJsScriptContributor)
},
ScriptFiles = new[]
{
"/Pages/Public/CmsKit/highlightOnLoad.js"
})]
public class DefaultPageViewComponent : AbpViewComponent
{
protected IPagePublicAppService PagePublicAppService { get; }
public DefaultPageViewComponent(IPagePublicAppService pagePublicAppService)
{
PagePublicAppService = pagePublicAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(Guid pageId, string title, string content)
{
var model = new PageViewModel
{
Id = pageId,
Title = title,
Content = content
};
return View("~/Pages/CmsKit/Shared/Components/Pages/Default.cshtml", model);
}
}
public class PageViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}

7
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs

@ -1,9 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.CmsKit.Tags;

115
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml

@ -1,5 +1,7 @@
@page
@using Volo.Abp.Data
@using Volo.CmsKit.Public.Blogs
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment
@using Volo.CmsKit.Public.Web.Pages
@using Volo.Abp.GlobalFeatures
@ -9,6 +11,7 @@
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags
@using Volo.CmsKit.Public.Web.Renderers
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs
@using Volo.CmsKit.Contents
@inherits CmsKitPublicPageBase
@ -23,71 +26,77 @@
}
@section styles{
@if (isScrollIndexEnabled)
{
<abp-abp-style-bundle>
<abp-style src="/Pages/Public/CmsKit/Blogs/bootstrap-toc.css"/>
</abp-abp-style-bundle>
}
@if (isScrollIndexEnabled)
{
<abp-abp-style-bundle>
<abp-style src="/Pages/Public/CmsKit/Blogs/bootstrap-toc.css" />
</abp-abp-style-bundle>
}
<abp-abp-style-bundle>
<abp-style src="/Pages/Public/CmsKit/Blogs/blogPost.css"/>
<abp-style type="typeof(HighlightJsStyleContributor)"/>
<abp-style src="/Pages/Public/CmsKit/Blogs/blogPost.css" />
<abp-style type="typeof(HighlightJsStyleContributor)" />
</abp-abp-style-bundle>
}
@section scripts{
@section scripts{
@if (isScrollIndexEnabled)
{
<abp-script-bundle>
<abp-style src="/Pages/Public/CmsKit/Blogs/bootstrap-toc.js"/>
<abp-script src="/Pages/Public/CmsKit/Blogs/blogpost-scroll-index.js"/>
<abp-style src="/Pages/Public/CmsKit/Blogs/bootstrap-toc.js" />
<abp-script src="/Pages/Public/CmsKit/Blogs/blogpost-scroll-index.js" />
</abp-script-bundle>
}
<abp-script-bundle>
<abp-script type="typeof(HighlightJsScriptContributor)"/>
<abp-script src="/Pages/Public/CmsKit/highlightOnLoad.js"/>
<abp-script type="typeof(HighlightJsScriptContributor)" />
<abp-script src="/Pages/Public/CmsKit/highlightOnLoad.js" />
</abp-script-bundle>
}
<div class="row">
<div @Html.Raw(isScrollIndexEnabled ? "class=\"col-md-10 col-sm-12\"" : "class=\"col-md-12\"")>
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'"/>
<abp-card-body>
<abp-row>
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1>
<p class="mb-lg-5 mb-md-3">
<a href="/blogs/@Model.BlogSlug?authorId=@Model.BlogPost.Author.Id">
<span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span>
</a>
<small style="opacity:.65;">@Model.BlogPost.CreationTime</small>
</p>
@if (!Model.BlogPost.Content.IsNullOrEmpty())
{
@Html.Raw(await MarkdownRenderer.RenderAsync(Model.BlogPost.Content))
}
<p class="mb-3">
@if (Model.BlogPost.LastModificationTime != null)
<div class="row">
<div @Html.Raw(isScrollIndexEnabled ? "class=\"col-md-10 col-sm-12\"" : "class=\"col-md-12\"")>
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
<abp-card-body>
<abp-row>
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1>
<p class="mb-lg-5 mb-md-3">
<a href="/blogs/@Model.BlogSlug?authorId=@Model.BlogPost.Author.Id">
<span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span>
</a>
<small style="opacity:.65;">@Model.BlogPost.CreationTime</small>
</p>
@foreach (var contentFragment in Model.BlogPost.ContentFragments)
{
<small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small>
if (contentFragment.Type == ContentConsts.Markdown)
{
@Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty<string>("Content")))
}
else if (contentFragment.Type == ContentConsts.Widget)
{
@await Component.InvokeAsync(contentFragment.GetProperty<string>("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
}
}
</p>
<hr/>
<p class="mb-3">
@if (Model.BlogPost.LastModificationTime != null)
{
<small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small>
}
</p>
<hr />
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
if (Model.TagsFeature?.IsEnabled == true)
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
if (Model.TagsFeature?.IsEnabled == true)
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString(),
urlFormat = $"/blogs/{Model.BlogSlug}?tagId={{TagId}}"
})
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString(),
urlFormat = $"/blogs/{Model.BlogSlug}?tagId={{TagId}}"
})
}
}
</div>
@ -100,10 +109,10 @@
if (Model.ReactionsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
</abp-column>
@ -113,10 +122,10 @@
if (Model.RatingsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
</abp-column>

8
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs

@ -1,11 +1,5 @@
using Markdig;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.GlobalFeatures;

41
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml

@ -1,31 +1,46 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.CmsKit.Localization
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@using Volo.CmsKit.Contents
@using System.Dynamic
@using Volo.CmsKit.Public.Web.Renderers
@using Volo.Abp.Data
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs;
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
@inject IMarkdownToHtmlRenderer MarkdownRenderer
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages.IndexModel
@inject IHtmlLocalizer<CmsKitResource> L
@section styles{
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" />
<style>
@Html.Raw(Model.Page.Style)
@Html.Raw(Model.PageDto.Style)
</style>
}
@section scripts{
<script>
@Html.Raw(Model.Page.Script)
@Html.Raw(Model.PageDto.Script)
</script>
}
@await Component.InvokeAsync(typeof(DefaultPageViewComponent),
new
{
pageId = Model.Page.Id,
title = Model.Page.Title,
content = Model.Page.Content
})
<abp-card>
<abp-card-body>
@foreach (ContentFragment contentFragment in Model.PageDto.ContentFragments)
{
if (contentFragment.Type == ContentConsts.Markdown)
{
@Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty<string>("Content")))
}
else if (contentFragment.Type == ContentConsts.Widget)
{
@await Component.InvokeAsync(contentFragment.GetProperty<string>("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
}
}
</abp-card-body>
</abp-card>

8
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs

@ -12,18 +12,18 @@ public class IndexModel : CommonPageModel
protected IPagePublicAppService PagePublicAppService { get; }
public PageDto Page;
public PageDto PageDto{ get; private set; }
public IndexModel(IPagePublicAppService pagePublicAppService)
public IndexModel(IPagePublicAppService pagePublicAppService)
{
PagePublicAppService = pagePublicAppService;
}
public async Task<IActionResult> OnGetAsync()
{
Page = await PagePublicAppService.FindBySlugAsync(Slug);
PageDto = await PagePublicAppService.FindBySlugAsync(Slug);
if (Page == null)
if (PageDto == null)
{
return NotFound();
}

14
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages;
public class PageViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public List<ContentFragment> ContentFragments { get; set; }
}

138
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs

@ -0,0 +1,138 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace Volo.CmsKit.Contents;
public class ContentParser_Test : CmsKitDomainTestBase
{
private readonly CmsKitTestData testData;
private readonly IOptions<CmsKitContentWidgetOptions> _options;
private ContentParser contentParser;
public ContentParser_Test()
{
testData = GetRequiredService<CmsKitTestData>();
_options = GetRequiredService<IOptions<CmsKitContentWidgetOptions>>();
}
[Fact]
public async Task ParseAsync_ShouldWorkWithDifferentWidgetTypes()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName);
_options.Value.AddWidget("ImageGallery", "ImageGallery");
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.
[Widget GalleryName=""Xyz"" Type=""ImageGallery"" Source=""GoogleDrive""]";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(4);
widgets[1].ExtraProperties.Count.ShouldBe(2);
widgets[3].ExtraProperties.Count.ShouldBe(3);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithoutConfigOptions()
{
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
contentParser = new ContentParser(_options);
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(1);//Ignored Widget
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongConfigOptions()
{
_options.Value.AddWidget(testData.WidgetName, testData.PollName);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(2);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongWidgetType()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Wrong Type= ""Poll"" Code =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(2);
}
[Fact]
public async Task ParseAsync_ShouldWorkWithWrongPollName()
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName);
contentParser = new ContentParser(_options);
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type= ""Poll"" PollWrongName =""poll-name""]
Thanks _for_ *your * feedback.";
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(3);
}
[Theory]
[MemberData(nameof(ExampleData))]
public async Task ParseAsync_ShouldWorkProperlyWithCorrectInputs(string content, int expectedLine)
{
_options.Value.AddWidget(testData.PollName, testData.WidgetName);
contentParser = new ContentParser(_options);
var widgets = await contentParser.ParseAsync(content);
widgets.ShouldNotBeNull();
widgets.Count.ShouldBe(expectedLine);
}
public static IEnumerable<object[]> ExampleData =>
new List<object[]>
{
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.", 3},
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
[Widget Type=""Poll"" Code=""poll-name""]
Thanks _for_ *your * feedback.
[Widget Type=""Poll"" Code=""poll-name1""]", 4 },
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
Thanks _for_ *your * feedback.
[Widget Type=""Poll"" Code=""poll-name""]", 2 },
new object[] { @"[Widget Type=""Poll"" Code=""poll-name""] gg [Widget Type=""Poll"" Code=""poll-name1""]**ABP Framework** is completely open source and developed in a community-driven manner.
Thanks _for_ *your * feedback.
Thanks _for_ *your * feedback.", 4},
new object[] { @"Thanks _for_ *your * feedback.
Thanks _for_ *your * feedback.", 1}
};
}

13
modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Pages/PageManager_Test.cs

@ -1,9 +1,6 @@
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.CmsKit.Pages;
@ -20,6 +17,7 @@ public class PageManager_Test : CmsKitDomainTestBase
pageRepository = GetRequiredService<IPageRepository>();
}
[Fact]
public async Task CreateAsync_ShouldWorkProperly_WithNonExistingSlug()
{
var title = "My awesome page";
@ -34,6 +32,7 @@ public class PageManager_Test : CmsKitDomainTestBase
page.Content.ShouldBe(content);
}
[Fact]
public async Task CreateAsync_ShouldThrowException_WithExistingSlug()
{
var title = "My awesome page";
@ -46,6 +45,7 @@ public class PageManager_Test : CmsKitDomainTestBase
exception.ShouldNotBeNull();
}
[Fact]
public async Task SetSlugAsync_ShouldWorkProperly_WithNonExistingSlug()
{
var newSlug = "freshly-generated-new-slug";
@ -56,6 +56,7 @@ public class PageManager_Test : CmsKitDomainTestBase
page.Slug.ShouldBe(newSlug);
}
[Fact]
public async Task SetSlugAsync_ShouldThrowException_WithExistingSlug()
{
var newSlug = testData.Page_2_Slug;

4
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs

@ -127,4 +127,8 @@ public class CmsKitTestData : ISingletonDependency
public string MenuItem_3_Name { get; } = "Products";
public string MenuItem_3_Url { get; } = "/products";
public string PollName { get; } = "Poll";
public string WidgetName { get; } = "CmsPollByCode";
}

Loading…
Cancel
Save