-
@Model.BlogPost.Title
-
-
- @@@Model.BlogPost.Author?.UserName
-
- @Model.BlogPost.CreationTime
-
- @if (!Model.BlogPost.Content.IsNullOrEmpty())
- {
- @Html.Raw(await MarkdownRenderer.RenderAsync(Model.BlogPost.Content))
- }
-
-
- @if (Model.BlogPost.LastModificationTime != null)
+
+
+
+
+
+
+
+
@Model.BlogPost.Title
+
+
+ @@@Model.BlogPost.Author?.UserName
+
+ @Model.BlogPost.CreationTime
+
+ @foreach (var contentFragment in Model.BlogPost.ContentFragments)
{
-
@L["LastModification"].Value : @Model.BlogPost.LastModificationTime
+ if (contentFragment.Type == ContentConsts.Markdown)
+ {
+ @Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty
("Content")))
+ }
+ else if (contentFragment.Type == ContentConsts.Widget)
+ {
+ @await Component.InvokeAsync(contentFragment.GetProperty("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
+ }
}
-
-
+
+ @if (Model.BlogPost.LastModificationTime != null)
+ {
+ @L["LastModification"].Value : @Model.BlogPost.LastModificationTime
+ }
+
+
- @if (GlobalFeatureManager.Instance.IsEnabled())
- {
- if (Model.TagsFeature?.IsEnabled == true)
+ @if (GlobalFeatureManager.Instance.IsEnabled())
{
- @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}}"
+ })
}
}
@@ -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()
+ })
}
}
@@ -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()
+ })
}
}
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 dcefb976a4..6227190ab8 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,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;
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
index a3f1d0c95e..5a92ae2b04 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
+++ b/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 L
@section styles{
}
@section scripts{
}
-@await Component.InvokeAsync(typeof(DefaultPageViewComponent),
- new
- {
- pageId = Model.Page.Id,
- title = Model.Page.Title,
- content = Model.Page.Content
- })
\ No newline at end of file
+
+
+ @foreach (ContentFragment contentFragment in Model.PageDto.ContentFragments)
+ {
+ if (contentFragment.Type == ContentConsts.Markdown)
+ {
+ @Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty("Content")))
+ }
+ else if (contentFragment.Type == ContentConsts.Widget)
+ {
+ @await Component.InvokeAsync(contentFragment.GetProperty("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject())
+ }
+ }
+
+
+
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs
index 6eeac638cf..1434eccb4b 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs
+++ b/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 OnGetAsync()
{
- Page = await PagePublicAppService.FindBySlugAsync(Slug);
+ PageDto = await PagePublicAppService.FindBySlugAsync(Slug);
- if (Page == null)
+ if (PageDto == null)
{
return NotFound();
}
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs
new file mode 100644
index 0000000000..5e5df3e917
--- /dev/null
+++ b/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 ContentFragments { get; set; }
+}
\ No newline at end of file
diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs
new file mode 100644
index 0000000000..108d883a70
--- /dev/null
+++ b/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 _options;
+ private ContentParser contentParser;
+
+ public ContentParser_Test()
+ {
+ testData = GetRequiredService();
+ _options = GetRequiredService>();
+ }
+
+ [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