Browse Source

Merge pull request #17925 from abpframework/cms-kit-enhancements

CMS Kit Enhancements
pull/18063/head
Alper Ebiçoğlu 3 years ago
committed by GitHub
parent
commit
c9a84ceeb5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      docs/en/Modules/Cms-Kit/Blogging.md
  2. 1
      docs/en/Modules/Cms-Kit/Comments.md
  3. 3
      docs/en/Modules/Cms-Kit/Ratings.md
  4. 24
      modules/cms-kit/README.md
  5. 15
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  6. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json
  7. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Blogs/CreateModal.cshtml
  8. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Blogs/UpdateModal.cshtml
  9. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml
  10. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml
  11. 28
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/Blogs/BlogFeatureClientProxy.Generated.cs
  12. 7
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/Blogs/BlogFeatureClientProxy.cs
  13. 28
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/MediaDescriptors/MediaDescriptorClientProxy.Generated.cs
  14. 7
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/MediaDescriptors/MediaDescriptorClientProxy.cs
  15. 54
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/cms-kit-common-generate-proxy.json
  16. 6
      modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsBlogsWebConsts.cs
  17. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json
  18. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json
  19. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json
  20. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json
  21. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  22. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json
  23. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json
  24. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json
  25. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json
  26. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json
  27. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json
  28. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json
  29. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json
  30. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json
  31. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json
  32. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json
  33. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json
  34. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json
  35. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json
  36. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  37. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json
  38. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json
  39. 1
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json
  40. 3
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs
  41. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  42. 8
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Blogs/BlogPostPublicClientProxy.Generated.cs
  43. 66
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  44. 8
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs
  45. 12
      modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs
  46. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs
  47. 176
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
  48. 91
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/Default.cshtml
  49. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingViewComponent.cs
  50. 3
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.js
  51. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml
  52. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  53. 71
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml
  54. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml.cs
  55. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/bootstrap-toc.css
  56. 15
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostPublicAppService_Tests.cs

11
docs/en/Modules/Cms-Kit/Blogging.md

@ -29,7 +29,16 @@ A screenshot from the new blog creation modal:
![blogs-edit](../../images/cmskit-module-blogs-edit.png)
**Slug** is the URL part of the blog. For this example, the root URL of the blog becomes *https://your-domain.com/blogs/technical-blog/*.
**Slug** is the URL part of the blog. For this example, the root URL of the blog becomes `your-domain.com/blogs/technical-blog/`.
- You can change the default slug by using `CmsBlogsWebConsts.BlogRoutePrefix` constant. For example, if you set it to `foo`, the root URL of the blog becomes `your-domain.com/foo/technical-blog/`.
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
CmsBlogsWebConsts.BlogsRoutePrefix = "foo";
}
```
#### Blog Features

1
docs/en/Modules/Cms-Kit/Comments.md

@ -53,6 +53,7 @@ The comment system provides a commenting [widget](../../UI/AspNetCore/Widgets.md
{
entityType = "Product",
entityId = "...",
isReadOnly = false,
referralLinks = new [] {"nofollow"}
})
```

3
docs/en/Modules/Cms-Kit/Ratings.md

@ -41,7 +41,8 @@ The ratings system provides a rating widget to allow users send ratings to resou
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = "Product",
entityId = "entityId"
entityId = "entityId",
isReadOnly = false
})
```

24
modules/cms-kit/README.md

@ -0,0 +1,24 @@
# CMS Kit
## Updating Client Proxies
This project have 3 types of client proxies. Before updating client proxies, make sure `Volo.CmsKit.Web.Unified` project is running.
Then you can update Client proxies in 3 different projects. Execute the following commands in the directory of the each project.
- CMS Kit Public (**Volo.CmsKit.Public.HttpApi.Client**)
```bash
abp generate-proxy -t csharp -url https://localhost:44349 -m cms-kit --without-contracts
```
- CMS Kit Common (**Volo.CmsKit.Common.HttpApi.Client**)
```bash
abp generate-proxy -t csharp -url https://localhost:44349 -m cms-kit-common --without-contracts
```
- CMS Kit Admin (**Volo.CmsKit.Admin.HttpApi.Client**)
```bash
abp generate-proxy -t csharp -url https://localhost:44349 -m cms-kit-admin --without-contracts
```

15
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml

@ -48,7 +48,12 @@
<abp-column size-md="_6">
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new {entityType = "quote", entityId = "1"})
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = "quote",
entityId = "1",
isReadOnly = false
})
}
</abp-column>
<abp-column size-md="_6">
@ -60,7 +65,13 @@
<abp-column size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
@await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "1", referralLinks = new [] {"nofollow"}})
@await Component.InvokeAsync(typeof(CommentingViewComponent), new
{
entityType = "quote",
entityId = "1",
isReadOnly = false,
referralLinks = new [] {"nofollow"}
})
}
</abp-column>
</abp-row>

4
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json

@ -2873,8 +2873,8 @@
"nameOnMethod": "input",
"name": "Status",
"jsonName": null,
"type": "Volo.CmsKit.Blogs.BlogPostStatus?",
"typeSimple": "Volo.CmsKit.Blogs.BlogPostStatus?",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,

3
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Blogs/CreateModal.cshtml

@ -9,6 +9,7 @@
@using Volo.Abp.Data
@using Volo.Abp.Localization
@using Volo.Abp.ObjectExtending
@using Volo.CmsKit.Web
@inject IStringLocalizerFactory StringLocalizerFactory
@inherits CmsKitAdminPageBase
@ -25,7 +26,7 @@
<abp-modal-body>
<abp-input asp-for="ViewModel.Name" />
<abp-input asp-for="ViewModel.Slug" />
<abp-input asp-for="ViewModel.Slug" label-tooltip-icon="fa fa-info-circle" label-tooltip="@L.GetString("BlogSlugInformation", CmsBlogsWebConsts.BlogsRoutePrefix)" />
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.CreateBlogViewModel>())
{

3
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Blogs/UpdateModal.cshtml

@ -8,6 +8,7 @@
@using Volo.Abp.Data
@using Volo.Abp.Localization
@using Volo.Abp.ObjectExtending
@using Volo.CmsKit.Web
@inject IStringLocalizerFactory StringLocalizerFactory
@inherits CmsKitAdminPageBase
@ -26,7 +27,7 @@
<abp-input asp-for="ViewModel.Name" />
<abp-input asp-for="ViewModel.Slug" />
<abp-input asp-for="ViewModel.Slug" label-tooltip-icon="fa fa-info-circle" label-tooltip="@L.GetString("BlogSlugInformation", CmsBlogsWebConsts.BlogsRoutePrefix)" />
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<UpdateModalModel.UpdateBlogViewModel>())
{

2
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml

@ -59,7 +59,7 @@
<form asp-page="/CmsKit/Pages/Create" id="form-page-create">
<abp-input asp-for="@Model.ViewModel.Title" />
<abp-input asp-for="ViewModel.Slug" title="@L["PageSlugInformation"]" data-toggle="tooltip" />
<abp-input asp-for="ViewModel.Slug" label-tooltip-icon="fa fa-info-circle" label-tooltip="@L["PageSlugInformation"]" />
<abp-input asp-for="@Model.ViewModel.Content" />

2
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml

@ -64,7 +64,7 @@
<abp-input asp-for="@Model.ViewModel.Title" />
<abp-input asp-for="ViewModel.Slug" title="@L["PageSlugInformation"]" data-toggle="tooltip" />
<abp-input asp-for="ViewModel.Slug" label-tooltip-icon="fa fa-info-circle" label-tooltip="@L["PageSlugInformation"]" />
<abp-input asp-for="@Model.ViewModel.Content" />

28
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/Blogs/BlogFeatureClientProxy.Generated.cs

@ -0,0 +1,28 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Modeling;
using Volo.CmsKit.Blogs;
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.Blogs;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBlogFeatureAppService), typeof(BlogFeatureClientProxy))]
public partial class BlogFeatureClientProxy : ClientProxyBase<IBlogFeatureAppService>, IBlogFeatureAppService
{
public virtual async Task<BlogFeatureDto> GetOrDefaultAsync(Guid blogId, string featureName)
{
return await RequestAsync<BlogFeatureDto>(nameof(GetOrDefaultAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), blogId },
{ typeof(string), featureName }
});
}
}

7
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/Blogs/BlogFeatureClientProxy.cs

@ -0,0 +1,7 @@
// This file is part of BlogFeatureClientProxy, you can customize it here
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.Blogs;
public partial class BlogFeatureClientProxy
{
}

28
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/MediaDescriptors/MediaDescriptorClientProxy.Generated.cs

@ -0,0 +1,28 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Modeling;
using Volo.CmsKit.MediaDescriptors;
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.MediaDescriptors;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IMediaDescriptorAppService), typeof(MediaDescriptorClientProxy))]
public partial class MediaDescriptorClientProxy : ClientProxyBase<IMediaDescriptorAppService>, IMediaDescriptorAppService
{
public virtual async Task<RemoteStreamContent> DownloadAsync(Guid id)
{
return await RequestAsync<RemoteStreamContent>(nameof(DownloadAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
}

7
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/Volo/CmsKit/MediaDescriptors/MediaDescriptorClientProxy.cs

@ -0,0 +1,7 @@
// This file is part of MediaDescriptorClientProxy, you can customize it here
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.MediaDescriptors;
public partial class MediaDescriptorClientProxy
{
}

54
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi.Client/ClientProxies/cms-kit-common-generate-proxy.json

@ -8,11 +8,32 @@
"controllerName": "MediaDescriptor",
"controllerGroupName": "MediaDescriptor",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.CmsKit.MediaDescriptors.MediaDescriptorController",
"interfaces": [
{
"type": "Volo.CmsKit.MediaDescriptors.IMediaDescriptorAppService"
"type": "Volo.CmsKit.MediaDescriptors.IMediaDescriptorAppService",
"name": "IMediaDescriptorAppService",
"methods": [
{
"name": "DownloadAsync",
"parametersOnMethod": [
{
"name": "id",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Abp.Content.RemoteStreamContent",
"typeSimple": "Volo.Abp.Content.RemoteStreamContent"
}
}
]
}
],
"actions": {
@ -59,11 +80,40 @@
"controllerName": "BlogFeature",
"controllerGroupName": "BlogFeature",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.CmsKit.Blogs.BlogFeatureController",
"interfaces": [
{
"type": "Volo.CmsKit.Blogs.IBlogFeatureAppService"
"type": "Volo.CmsKit.Blogs.IBlogFeatureAppService",
"name": "IBlogFeatureAppService",
"methods": [
{
"name": "GetOrDefaultAsync",
"parametersOnMethod": [
{
"name": "blogId",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "featureName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.CmsKit.Blogs.BlogFeatureDto",
"typeSimple": "Volo.CmsKit.Blogs.BlogFeatureDto"
}
}
]
}
],
"actions": {

6
modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsBlogsWebConsts.cs

@ -0,0 +1,6 @@
namespace Volo.CmsKit.Web;
public static class CmsBlogsWebConsts
{
public static string BlogsRoutePrefix { get; set; } = "blogs";
}

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ar.json

@ -77,6 +77,7 @@
"PageId": "صفحة",
"Pages": "الصفحات",
"PageSlugInformation": "سبيكة تستخدم على url. سيكون عنوان url الخاص بك هو \"/{slug}}\".",
"BlogSlugInformation": "سبيكة تستخدم على url. سيكون عنوان url الخاص بك هو \"/{0}/{slug}}\".",
"Permission:BlogManagement": "إدارة المدونة",
"Permission:BlogManagement.Create": "إنشاء",
"Permission:BlogManagement.Delete": "حذف",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json

@ -77,6 +77,7 @@
"PageId": "Strana",
"Pages": "stránky",
"PageSlugInformation": "Na adrese URL je použit Slug. Vaše adresa URL bude '/{{slug}}'.",
"BlogSlugInformation": "Na adrese URL je použit Slug. Vaše adresa URL bude '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Správa blogu",
"Permission:BlogManagement.Create": "Vytvořit",
"Permission:BlogManagement.Delete": "Vymazat",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/de-DE.json

@ -77,6 +77,7 @@
"PageId": "Buchseite",
"Pages": "Seiten",
"PageSlugInformation": "Slug wird auf URL verwendet. Ihre URL lautet '/{{slug}}'.",
"BlogSlugInformation": "Slug wird auf URL verwendet. Ihre URL lautet '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blog-Verwaltung",
"Permission:BlogManagement.Create": "Schaffen",
"Permission:BlogManagement.Delete": "Löschen",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/el.json

@ -77,6 +77,7 @@
"PageId": "Σελίδα",
"Pages": "Σελίδες",
"PageSlugInformation": "Το Slug χρησιμοποιείται στο url. Το url σας θα είναι '/{{slug}}'.",
"BlogSlugInformation": "Το Slug χρησιμοποιείται στο url. Το url σας θα είναι '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Διαχείριση ιστολογίου",
"Permission:BlogManagement.Create": "Δημιουργώ",
"Permission:BlogManagement.Delete": "Διαγράφω",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json

@ -76,6 +76,7 @@
"PageId": "Page",
"Pages": "Pages",
"PageSlugInformation": "Slug is used on url. Your url will be '/{{slug}}'.",
"BlogSlugInformation": "Slug is used on url. Your url will be '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blog Management",
"Permission:BlogManagement.Create": "Create",
"Permission:BlogManagement.Delete": "Delete",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/es.json

@ -77,6 +77,7 @@
"PageId": "Página",
"Pages": "Paginas",
"PageSlugInformation": "Slug se usa en la URL. Su URL será '/{{slug}}'.",
"BlogSlugInformation": "Slug se usa en la URL. Su URL será '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Gestión de blogs",
"Permission:BlogManagement.Create": "Crear",
"Permission:BlogManagement.Delete": "Borrar",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fi.json

@ -76,6 +76,7 @@
"PageId": "Sivu",
"Pages": "Sivut",
"PageSlugInformation": "Etanaa käytetään URL-osoitteessa. URL-osoitteesi on '/{{slug}}'.",
"BlogSlugInformation": "Etanaa käytetään URL-osoitteessa. URL-osoitteesi on '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blogin hallinta",
"Permission:BlogManagement.Create": "Luonti",
"Permission:BlogManagement.Delete": "Poisto",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/fr.json

@ -77,6 +77,7 @@
"PageId": "Page",
"Pages": "Pages",
"PageSlugInformation": "Slug est utilisé sur l'url. Votre URL sera '/{{slug}}'.",
"BlogSlugInformation": "Slug est utilisé sur l'url. Votre URL sera '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Gestion de blog",
"Permission:BlogManagement.Create": "Créer",
"Permission:BlogManagement.Delete": "Effacer",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hi.json

@ -77,6 +77,7 @@
"PageId": "पृष्ठ",
"Pages": "पृष्ठों",
"PageSlugInformation": "स्लग का उपयोग url पर किया जाता है। आपका url '/{{slug}}' होगा।",
"BlogSlugInformation": "स्लग का उपयोग url पर किया जाता है। आपका url '/{0}/{{slug}}' होगा।",
"Permission:BlogManagement": "ब्लॉग प्रबंधन",
"Permission:BlogManagement.Create": "सृजन करना",
"Permission:BlogManagement.Delete": "हटाएं",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/hu.json

@ -76,6 +76,7 @@
"PageId": "oldal",
"Pages": "Oldalak",
"PageSlugInformation": "A Slug az url-en használatos. Az Ön URL-je a következő lesz: '/{{slug}}'.",
"BlogSlugInformation": "A Slug az url-en használatos. Az Ön URL-je a következő lesz: '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blogkezelés",
"Permission:BlogManagement.Create": "Teremt",
"Permission:BlogManagement.Delete": "Töröl",

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/is.json

@ -76,7 +76,8 @@
"PageDeletionConfirmationMessage": "Ertu viss um að eyða þessari síðu?",
"PageId": "Síða",
"Pages": "Síður",
"PageSlugInformation": "Snigill er notaður á slóð. Slóðin þín verður '/síður/{{slug}}'.",
"PageSlugInformation": "Snigill er notaður á slóð. Slóðin þín verður '/{{slug}}'.",
"BlogSlugInformation": "Snigill er notaður á slóð. Slóðin þín verður '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Bloggstjórnun",
"Permission:BlogManagement.Create": "Búa til",
"Permission:BlogManagement.Delete": "Eyða",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/it.json

@ -77,6 +77,7 @@
"PageId": "Pagina",
"Pages": "Pagine",
"PageSlugInformation": "Lo slug viene utilizzato sull'URL. Il tuo URL sarà '/{{slug}}'.",
"BlogSlugInformation": "Lo slug viene utilizzato sull'URL. Il tuo URL sarà '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Gestione del blog",
"Permission:BlogManagement.Create": "Crea",
"Permission:BlogManagement.Delete": "Elimina",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/nl.json

@ -77,6 +77,7 @@
"PageId": "Bladzijde",
"Pages": "Pagina's",
"PageSlugInformation": "Slug wordt gebruikt voor de url. Uw url wordt '/{{slug}}'.",
"BlogSlugInformation": "Slug wordt gebruikt voor de url. Uw url wordt '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blogbeheer",
"Permission:BlogManagement.Create": "Toevoegen",
"Permission:BlogManagement.Delete": "Verwijderen",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json

@ -77,6 +77,7 @@
"PageId": "Strona",
"Pages": "Strony",
"PageSlugInformation": "Slug jest używany na adresie URL. Twój adres URL to „/{{slug}}”.",
"BlogSlugInformation": "Slug jest używany na adresie URL. Twój adres URL to „/{0}/{{slug}}”.",
"Permission:BlogManagement": "Zarządzanie blogiem",
"Permission:BlogManagement.Create": "Tworzyć",
"Permission:BlogManagement.Delete": "Kasować",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json

@ -77,6 +77,7 @@
"PageId": "Página",
"Pages": "Páginas",
"PageSlugInformation": "Slug é usado na url. Sua url será '/{{slug}}'.",
"BlogSlugInformation": "Slug é usado na url. Sua url será '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Gerenciamento de blogs",
"Permission:BlogManagement.Create": "Criar",
"Permission:BlogManagement.Delete": "Excluir",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ro-RO.json

@ -77,6 +77,7 @@
"PageId": "Pagina",
"Pages": "Pagini",
"PageSlugInformation": "Slug este folosit pe url. Url-ul dumneavoastră va fi '/{{slug}}'.",
"BlogSlugInformation": "Slug este folosit pe url. Url-ul dumneavoastră va fi '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Administrare Blog",
"Permission:BlogManagement.Create": "Creează",
"Permission:BlogManagement.Delete": "Şterge",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/ru.json

@ -77,6 +77,7 @@
"PageId": "Страница",
"Pages": "Страницы",
"PageSlugInformation": "Слаг используется для URL. Ваш URL-адрес будет '/{{slug}}'.",
"BlogSlugInformation": "Слаг используется для URL. Ваш URL-адрес будет '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Управление блогом",
"Permission:BlogManagement.Create": "Создавать",
"Permission:BlogManagement.Delete": "Удалить",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sk.json

@ -77,6 +77,7 @@
"PageId": "Stránka",
"Pages": "Stránky",
"PageSlugInformation": "Slug sa používa v URL. Vaša URL bude '/{{slug}}'.",
"BlogSlugInformation": "Slug sa používa v URL. Vaša URL bude '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Správa blogov",
"Permission:BlogManagement.Create": "Vytvoriť",
"Permission:BlogManagement.Delete": "Zmazať",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json

@ -77,6 +77,7 @@
"PageId": "stran",
"Pages": "strani",
"PageSlugInformation": "Slug se uporablja na url-ju. Vaš url bo '/{{slug}}'.",
"BlogSlugInformation": "Slug se uporablja na url-ju. Vaš url bo '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Upravljanje blogov",
"Permission:BlogManagement.Create": "Ustvari",
"Permission:BlogManagement.Delete": "Izbriši",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json

@ -76,6 +76,7 @@
"PageId": "Sayfa",
"Pages": "Sayfalar",
"PageSlugInformation": "Etiket URL'de kullanılır. Url şöyle görünür: '/{{slug}}'.",
"BlogSlugInformation": "Etiket URL'de kullanılır. Url şöyle görünür: '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Blog Yönetimi",
"Permission:BlogManagement.Create": "Oluşturma",
"Permission:BlogManagement.Delete": "Silme",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json

@ -77,6 +77,7 @@
"PageId": "Trang",
"Pages": "Các trang",
"PageSlugInformation": "Slug được sử dụng trên url. Url của bạn sẽ là '/{{slug}}'.",
"BlogSlugInformation": "Slug được sử dụng trên url. Url của bạn sẽ là '/{0}/{{slug}}'.",
"Permission:BlogManagement": "Quản lý blog",
"Permission:BlogManagement.Create": "Tạo ra",
"Permission:BlogManagement.Delete": "Xóa bỏ",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json

@ -76,6 +76,7 @@
"PageId": "页",
"Pages": "页面",
"PageSlugInformation": "Slug用于url. 你的url将是 '/{{slug}}'.",
"BlogSlugInformation": "Slug用于url. 你的url将是 '/{0}/{{slug}}'.",
"Permission:BlogManagement": "博客管理",
"Permission:BlogManagement.Create": "创建",
"Permission:BlogManagement.Delete": "删除",

1
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json

@ -77,6 +77,7 @@
"PageId": "頁",
"Pages": "頁面",
"PageSlugInformation": "Slug用於網址. 你的網址將是 '/{{slug}}'.",
"BlogSlugInformation": "Slug用於網址. 你的網址將是 '/{0}/{{slug}}'.",
"Permission:BlogManagement": "部落格管理",
"Permission:BlogManagement.Create": "創建",
"Permission:BlogManagement.Delete": "刪除",

3
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs

@ -17,5 +17,8 @@ public interface IBlogPostPublicAppService : IApplicationService
Task<PagedResultDto<CmsUserDto>> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input);
Task<CmsUserDto> GetAuthorHasBlogPostAsync(Guid id);
Task DeleteAsync(Guid id);
Task<string> GetTagNameAsync([NotNull] Guid tagId);
}

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

@ -12,6 +12,7 @@ using Volo.CmsKit.Blogs;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Features;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Public.Blogs;
@ -24,12 +25,16 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
protected IBlogPostRepository BlogPostRepository { get; }
protected ITagRepository TagRepository { get; }
public BlogPostPublicAppService(
IBlogRepository blogRepository,
IBlogPostRepository blogPostRepository)
IBlogPostRepository blogPostRepository,
ITagRepository tagRepository)
{
BlogRepository = blogRepository;
BlogPostRepository = blogPostRepository;
TagRepository = tagRepository;
}
public virtual async Task<BlogPostCommonDto> GetAsync(
@ -85,4 +90,11 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
await BlogPostRepository.DeleteAsync(id);
}
public async Task<string> GetTagNameAsync([NotNull] Guid tagId)
{
var tag = await TagRepository.GetAsync(tagId);
return tag.Name;
}
}

8
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Blogs/BlogPostPublicClientProxy.Generated.cs

@ -60,4 +60,12 @@ public partial class BlogPostPublicClientProxy : ClientProxyBase<IBlogPostPublic
{ typeof(Guid), id }
});
}
public virtual async Task<string> GetTagNameAsync(Guid tagId)
{
return await RequestAsync<string>(nameof(GetTagNameAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), tagId }
});
}
}

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

@ -1512,6 +1512,23 @@
"type": "System.Void",
"typeSimple": "System.Void"
}
},
{
"name": "GetTagNameAsync",
"parametersOnMethod": [
{
"name": "tagId",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
}
}
]
}
@ -1825,6 +1842,55 @@
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService"
},
"GetTagNameAsyncByTagId": {
"uniqueName": "GetTagNameAsyncByTagId",
"name": "GetTagNameAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/blog-posts/tags/{id}",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "tagId",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "tagId",
"name": "tagId",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": ""
},
{
"nameOnMethod": "id",
"name": "id",
"jsonName": null,
"type": null,
"typeSimple": null,
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService"
}
}
}

8
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
@ -60,4 +61,11 @@ public class BlogPostPublicController : CmsKitPublicControllerBase, IBlogPostPub
{
return BlogPostPublicAppService.DeleteAsync(id);
}
[HttpGet]
[Route("tags/{id}")]
public Task<string> GetTagNameAsync([NotNull] Guid tagId)
{
return BlogPostPublicAppService.GetTagNameAsync(tagId);
}
}

12
modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Localization;
@ -91,8 +92,13 @@ public class CmsKitPublicWebModule : AbpModule
{
Configure<RazorPagesOptions>(options =>
{
options.Conventions.AddPageRoute("/Public/CmsKit/Blogs/Index", @"/blogs/{blogSlug:minlength(1)}");
options.Conventions.AddPageRoute("/Public/CmsKit/Blogs/BlogPost", @"/blogs/{blogSlug}/{blogPostSlug:minlength(1)}");
options.Conventions.AddPageRoute(
"/Public/CmsKit/Blogs/Index",
CmsBlogsWebConsts.BlogsRoutePrefix.EnsureStartsWith('/') + @"/{blogSlug:minlength(1)}");
options.Conventions.AddPageRoute(
"/Public/CmsKit/Blogs/BlogPost",
CmsBlogsWebConsts.BlogsRoutePrefix.EnsureStartsWith('/') + @"/{blogSlug}/{blogPostSlug:minlength(1)}");
});
}

6
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs

@ -60,6 +60,7 @@ public class CommentingViewComponent : AbpViewComponent
public virtual async Task<IViewComponentResult> InvokeAsync(
string entityType,
string entityId,
bool isReadOnly,
IEnumerable<string> referralLinks = null)
{
referralLinks ??= Enumerable.Empty<string>();
@ -74,7 +75,8 @@ public class CommentingViewComponent : AbpViewComponent
EntityType = entityType,
ReferralLinks = referralLinks,
LoginUrl = loginUrl,
Comments = comments.OrderByDescending(i => i.CreationTime).ToList()
IsReadOnly = isReadOnly,
Comments = comments.OrderByDescending(i => i.CreationTime).ToList(),
};
await ConvertMarkdownTextsToHtml(viewModel);
@ -144,6 +146,8 @@ public class CommentingViewComponent : AbpViewComponent
public string Captcha { get; set; }
public string CaptchaImageBase64 { get; set; }
public bool IsReadOnly { get; set; }
}
}

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

@ -20,16 +20,16 @@
@<span>
<i class="far fa-comment-alt me-2"></i>
@((string.IsNullOrWhiteSpace(author.Name)
? author.UserName
: author.Name + " " + author.Surname).Trim())
? author.UserName
: author.Name + " " + author.Surname).Trim())
<small class="text-muted float-end" style="opacity: .5; font-size: 14px">@creationTime.ToString()</small>
</span>;
}
@{
Func<dynamic, IHtmlContent> GetCommentArea(Guid? repliedCommentId, bool cancelButton = false) =>
@<div class="cms-comment-form-area bg-light card p-3 mx-0 @(repliedCommentId.HasValue ? "my-3" : "mt-3")"
data-reply-id="@(repliedCommentId?.ToString() ?? "")"
style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")">
data-reply-id="@(repliedCommentId?.ToString() ?? "")"
style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")">
<form class="cms-comment-form">
<input hidden value="@(Guid.NewGuid().ToString("N"))" name="idempotencyToken" />
<input hidden value="@(repliedCommentId?.ToString() ?? "")" name="repliedCommentId" />
@ -44,31 +44,31 @@
</div>
@if (CmsKitCommentOptions.Value.IsRecaptchaEnabled)
{
<div class="volo-captcha">
<label class="form-label" for="Input_Captcha">@L["CaptchaCode"]</label>
<div class="d-flex">
<div class="bd-highlight">
<img src="@Model.Input.CaptchaImageBase64" />
</div>
<div class="flex-grow-1 bd-highlight">
<abp-input type="number" asp-for="@Model.Input.Captcha" suppress-label="true" class="d-inline-block" autocomplete="off" />
</div>
<abp-input asp-for="@Model.CaptchaId" value="@Model.CaptchaOutput.Id" />
{
<div class="volo-captcha">
<label class="form-label" for="Input_Captcha">@L["CaptchaCode"]</label>
<div class="d-flex">
<div class="bd-highlight">
<img src="@Model.Input.CaptchaImageBase64" />
</div>
<div class="flex-grow-1 bd-highlight">
<abp-input type="number" asp-for="@Model.Input.Captcha" suppress-label="true" class="d-inline-block" autocomplete="off" />
</div>
<abp-input asp-for="@Model.CaptchaId" value="@Model.CaptchaOutput.Id" />
</div>
}
</div>
}
<div class="col-auto">
<div class="text-end">
<abp-button id="submit-button" type="submit" button-type="Primary">
<i class="fa fa-comment-alt me-1"></i> @L["Send"]
</abp-button>
@if (cancelButton)
{
<abp-button type="button" button-type="Light" class="reply-cancel-button" data-reply-id="@(repliedCommentId?.ToString() ?? "")">
<i class="fa fa-times me-1"></i> @L["Cancel"]
</abp-button>
}
{
<abp-button type="button" button-type="Light" class="reply-cancel-button" data-reply-id="@(repliedCommentId?.ToString() ?? "")">
<i class="fa fa-times me-1"></i> @L["Cancel"]
</abp-button>
}
</div>
</div>
</div>
@ -87,32 +87,32 @@
Func<dynamic, IHtmlContent> GetCommentActionArea(Guid id, Guid authorId, bool isReply) =>
@<div class="inner-comment-buttons text-start">
@if (!isReply)
{
@if (CurrentUser.IsAuthenticated)
{
<a href="#" class="comment-links comment-reply-link btn btn-sm shadow-sm btn-primary" data-reply-id="@id.ToString()" id="@($"cms-comment_{Model.Input.EntityType}_{Model.Input.EntityId}_{id}_link")">
<i class="fa fa-reply mr -1"></i> @L["Reply"]
</a>
{
@if (CurrentUser.IsAuthenticated)
{
<a href="#" class="comment-links comment-reply-link btn btn-sm shadow-sm btn-primary" data-reply-id="@id.ToString()" id="@($"cms-comment_{Model.Input.EntityType}_{Model.Input.EntityId}_{id}_link")">
<i class="fa fa-reply mr -1"></i> @L["Reply"]
</a>
<a href="#" class="comment-links comment-delete-link btn btn-sm shadow-sm btn-link text-muted bg-white " data-author-id="@authorId.ToString()" data-id="@id.ToString()">
<i class="fa fa-trash mr -1"></i> @L["Delete"]
</a>
}
else
{
<a href="@(Model.Input.LoginUrl + "_" + id)" class="btn btn-sm btn-light shadow-sm">
@L["LoginToReply"]
</a>
}
}
<a href="#" class="comment-links comment-delete-link btn btn-sm shadow-sm btn-link text-muted bg-white " data-author-id="@authorId.ToString()" data-id="@id.ToString()">
<i class="fa fa-trash mr -1"></i> @L["Delete"]
</a>
}
else
{
<a href="@(Model.Input.LoginUrl + "_" + id)" class="btn btn-sm btn-light shadow-sm">
@L["LoginToReply"]
</a>
}
}
@if (authorId == CurrentUser.Id)
{
<a href="#" class="comment-links comment-edit-link btn btn-sm shadow-sm btn-link text-muted bg-white" data-id="@id.ToString()">
<i class="fa fa-pencil mr -1 "></i> @L["Edit"]
</a>
}
{
<a href="#" class="comment-links comment-edit-link btn btn-sm shadow-sm btn-link text-muted bg-white" data-id="@id.ToString()">
<i class="fa fa-pencil mr -1 "></i> @L["Edit"]
</a>
}
</div>;
}
@{
@ -129,26 +129,26 @@
</div>
</div>
<div class="mt-0">
<small class="text-muted float-end" >@L["MarkdownSupported"]</small>
<small class="text-muted float-end">@L["MarkdownSupported"]</small>
</div>
@if (CmsKitCommentOptions.Value.IsRecaptchaEnabled)
{
var output = await Model.GenerateCaptchaAsync();
{
var output = await Model.GenerateCaptchaAsync();
<div class="volo-captcha">
<label class="form-label" for="Input_Captcha_@output.Id">@L["CaptchaCode"]</label>
<div class="d-flex">
<div class="bd-highlight">
<img src="@Model.GetCaptchaImageBase64(output.ImageBytes)"/>
</div>
<div class="flex-grow-1 bd-highlight">
<abp-input id="Input_Captcha_@output.Id" type="number" asp-for="@Model.Input.Captcha" suppress-label="true" class="d-inline-block" autocomplete="off"/>
</div>
<abp-input asp-for="@Model.CaptchaId" value="@output.Id"/>
<div class="volo-captcha">
<label class="form-label" for="Input_Captcha_@output.Id">@L["CaptchaCode"]</label>
<div class="d-flex">
<div class="bd-highlight">
<img src="@Model.GetCaptchaImageBase64(output.ImageBytes)" />
</div>
<div class="flex-grow-1 bd-highlight">
<abp-input id="Input_Captcha_@output.Id" type="number" asp-for="@Model.Input.Captcha" suppress-label="true" class="d-inline-block" autocomplete="off" />
</div>
<abp-input asp-for="@Model.CaptchaId" value="@output.Id" />
</div>
}
</div>
}
<div class="col-auto">
<div class="text-end">
<abp-button type="submit" button-type="Primary" size="Block"> @L["Update"] </abp-button>
@ -184,22 +184,25 @@
@GetCommentContentArea(comment.Id, comment.Text).Invoke(null)
<div class="row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(comment.Id, comment.Author.Id, false).Invoke(null)
@if (!Model.Input.IsReadOnly)
{
<div class="row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(comment.Id, comment.Author.Id, false).Invoke(null)
</div>
</div>
</div>
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = comment.Id.ToString() })
}
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = comment.Id.ToString() })
}
</div>
</div>
</div>
</div>
@GetEditArea(comment.Id, Model.Input.RawCommentTexts[comment.Id], comment.ConcurrencyStamp).Invoke(null)
@GetEditArea(comment.Id, Model.Input.RawCommentTexts[comment.Id], comment.ConcurrencyStamp).Invoke(null)
}
@if (comment.Replies.Any())
{
@ -213,23 +216,26 @@
@GetCommentContentArea(reply.Id, reply.Text).Invoke(null)
<div class="row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(reply.Id, reply.Author.Id, true).Invoke(null)
@if (!Model.Input.IsReadOnly)
{
<div class="row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(reply.Id, reply.Author.Id, true).Invoke(null)
</div>
</div>
</div>
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = reply.Id.ToString() })
}
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = reply.Id.ToString() })
}
</div>
</div>
</div>
</div>
@GetEditArea(reply.Id, Model.Input.RawCommentTexts[reply.Id], reply.ConcurrencyStamp).Invoke(null)
@GetEditArea(reply.Id, Model.Input.RawCommentTexts[reply.Id], reply.ConcurrencyStamp).Invoke(null)
}
</div>
</div>
}

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

@ -6,58 +6,59 @@
@inject IHtmlLocalizer<CmsKitResource> L
<div class="row row">
<div class="col">
<div class="cms-rating-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId" id="cms-rating_{@Model.EntityType}_{@Model.EntityId}">
@if (CurrentUser.IsAuthenticated)
<div class="col">
<div class="cms-rating-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId" id="cms-rating_{@Model.EntityType}_{@Model.EntityId}">
@if (CurrentUser.IsAuthenticated)
{
@if (!Model.IsReadOnly && Model.CurrentRating != null)
{
@if (Model.CurrentRating != null)
{
<a href="#" class="rating-undo-link text-decoration-none">
<small class="text-muted"><i class="fa fa-undo"></i> @L["Undo"]</small>
</a>
}
if (Model.Ratings != null)
{
<a href="#" class="text-muted ms-1 text-decoration-none" data-bs-toggle="modal" data-bs-target="#ratingDetail">
<i class="far fa-question-circle"></i>
</a>
<a href="#" class="rating-undo-link text-decoration-none">
<small class="text-muted"><i class="fa fa-undo"></i> @L["Undo"]</small>
</a>
}
if (Model.Ratings != null)
{
<a href="#" class="text-muted ms-1 text-decoration-none" data-bs-toggle="modal" data-bs-target="#ratingDetail">
<i class="far fa-question-circle"></i>
</a>
<div class="modal fade" id="ratingDetail" tabindex="-1" role="dialog" aria-labelledby="ratingDetail" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Rating Detail</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row text-center">
@foreach (var rating in Model.Ratings)
{
<div class="col">
<label>@rating.StarCount @L["Star"]</label>
<div class="bar-container">
<div class="bar bar-@rating.StarCount" style="width: @(rating.Count * 100 / Model.TotalRating)%"></div>
</div>
<div><small class="text-muted">@rating.Count Rate(s)</small> </div>
<div class="modal fade" id="ratingDetail" tabindex="-1" role="dialog" aria-labelledby="ratingDetail" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Rating Detail</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row text-center">
@foreach (var rating in Model.Ratings)
{
<div class="col">
<label>@rating.StarCount @L["Star"]</label>
<div class="bar-container">
<div class="bar bar-@rating.StarCount" style="width: @(rating.Count * 100 / Model.TotalRating)%"></div>
</div>
}
</div>
<div><small class="text-muted">@rating.Count Rate(s)</small> </div>
</div>
}
</div>
</div>
</div>
</div>
}
</div>
}
<small class="live-rating text-center d-inline-block" style="width: 24px">@(Model.CurrentRating != null ? Model.CurrentRating + " " : 0 + "")</small>
<small class="live-rating text-center d-inline-block" style="width: 24px">@(Model.CurrentRating != null ? Model.CurrentRating + " " : 0 + "")</small>
<span class="my-rating text-dark p-1" data-rating="@(Model.CurrentRating ?? 0)" data-authenticated="@(Model.CurrentRating != null)">
</span>
}
else
{
<span class="my-rating text-dark p-1" data-authenticated="True" data-bs-toggle="popover" data-bs-placement="right" data-html="true" data-content="<div class='text-center'><div class='d-grid gap-2'><a href='@Model.LoginUrl' class='btn btn-primary'>@L["LoginToRate"]</a></div></div>"></span>
<span class="rating-login"></span>
}
</div>
<span class="my-rating text-dark p-1" data-rating="@(Model.CurrentRating ?? 0)" data-authenticated="@(Model.CurrentRating != null)" data-readonly="@Model.IsReadOnly">
</span>
}
else
{
<span class="my-rating text-dark p-1" data-authenticated="True" data-bs-toggle="popover" data-bs-placement="right" data-html="true" data-content="<div class='text-center'><div class='d-grid gap-2'><a href='@Model.LoginUrl' class='btn btn-primary'>@L["LoginToRate"]</a></div></div>"></span>
<span class="rating-login"></span>
}
</div>
</div>
</div>
</div>

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

@ -31,7 +31,7 @@ public class RatingViewComponent : AbpViewComponent
CurrentUser = currentUser;
}
public virtual async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId)
public virtual async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId, bool isReadOnly = false)
{
var ratings = await RatingPublicAppService.GetGroupedStarCountsAsync(entityType, entityId);
var totalRating = ratings.Sum(x => x.Count);
@ -52,7 +52,8 @@ public class RatingViewComponent : AbpViewComponent
LoginUrl = loginUrl,
Ratings = ratings,
CurrentRating = currentUserRating,
TotalRating = totalRating
TotalRating = totalRating,
IsReadOnly = isReadOnly
};
return View("~/Pages/CmsKit/Shared/Components/Rating/Default.cshtml", viewModel);
@ -72,4 +73,6 @@ public class RatingViewModel
public short? CurrentRating { get; set; }
public int TotalRating { get; set; }
public bool IsReadOnly { get; set; }
}

3
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.js

@ -15,6 +15,7 @@
function registerCreateOfNewRating() {
$widget.find(".my-rating").each(function () {
var authenticated = $(this).attr("data-authenticated");
var readonly = $(this).attr("data-readonly");
$(this).starRating({
initialRating: 0,
@ -26,7 +27,7 @@
strokeWidth: 0,
disableAfterRate: true,
useFullStars: true,
readOnly: authenticated === "True",
readOnly: authenticated === "True" || readonly === "True",
onHover: function (currentIndex, currentRating, $el) {
$widget.find(".live-rating").text(currentIndex);
},

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

@ -10,14 +10,14 @@
{
if (Model.UrlFormat.IsNullOrWhiteSpace())
{
<span class="badge rounded-pill bg-light text-dark px-3 py-2 cmskit-tag font-weight-normal">
<span class="badge rounded-pill badge-secondary text-bg-secondary cmskit-tag font-weight-normal">
@tag.Name
</span>
}
else
{
var formattedUrl = Model.UrlFormat.Replace("{TagId}", tag.Id.ToString()).Replace("{TagName}", tag.Name);
<a href="@formattedUrl"><span class="badge rounded-pill bg-light text-dark px-3 py-2 cmskit-tag font-weight-normal">@tag.Name</span></a>
<a href="@formattedUrl"><span class="badge rounded-pill badge-secondary text-bg-secondary rounded-pill cmskit-tag font-weight-normal">@tag.Name</span></a>
}
}
}

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

@ -10,6 +10,7 @@
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs
@using Volo.CmsKit.Contents
@using Volo.CmsKit.Web
@using Volo.CmsKit.Web.Pages.CmsKit.Components.Contents
@inherits CmsKitPublicPageBase
@ -62,7 +63,7 @@
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.ViewModel.Title</h1>
<p class="mb-lg-5 mb-md-3">
<a href="/blogs/@Model.BlogSlug?authorId=@Model.ViewModel.Author.Id">
<a href="/@CmsBlogsWebConsts.BlogsRoutePrefix/@Model.BlogSlug?authorId=@Model.ViewModel.Author.Id">
<span class="font-weight-bold">@@@Model.ViewModel.Author?.UserName</span>
</a>
<small style="opacity:.65;">@Model.ViewModel.CreationTime</small>
@ -89,7 +90,7 @@
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.ViewModel.Id.ToString(),
urlFormat = $"/blogs/{Model.BlogSlug}?tagId={{TagId}}"
urlFormat = $"/{CmsBlogsWebConsts.BlogsRoutePrefix}/{Model.BlogSlug}?tagId={{TagId}}"
})
}
}
@ -133,9 +134,11 @@
{
<button id="deleteBlogPost" class="btn btn-danger">
<i class="fa fa-remove">
@L["Delete"]
</i>
@L["Delete"]
</button>
<hr />
}
@if (isScrollIndexEnabled)
{

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

@ -2,18 +2,19 @@
@using Volo.CmsKit.Public.Web.Pages
@using Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs
@using Volo.CmsKit.Web
@inherits CmsKitPublicPageBase
@model IndexModel
@section styles{
<abp-style src="/Pages/Public/CmsKit/Blogs/index.css"/>
@section styles {
<abp-style src="/Pages/Public/CmsKit/Blogs/index.css" />
}
@section scripts {
<abp-script-bundle>
<abp-script src="/Pages/Public/CmsKit/Blogs/index.js"/>
<abp-script src="/Pages/Public/CmsKit/Blogs/index.js" />
</abp-script-bundle>
}
@ -21,29 +22,43 @@
const string dummyImageSource = "https://dummyimage.com/320x180/a3a3a3/fff.png";
}
<abp-row id="blogs-filter-area">
<abp-column size="_4">
<div class="mb-3">
<label class="form-label" asp-for="@Model.SelectedAuthor"></label>
@if (Model.AuthorId.HasValue)
{
<abp-row id="blogs-filter-area">
<abp-column size="_4">
<div class="mb-3">
<label class="form-label" asp-for="@Model.SelectedAuthor"></label>
<select id="AuthorSelect" asp-for="@Model.AuthorId"
class="auto-complete-select"
data-placeholder="@L["SelectAnAuthor"]"
data-allow-clear="true"
data-autocomplete-api-url="/api/cms-kit-public/blog-posts/authors"
data-autocomplete-display-property="userName"
data-autocomplete-value-property="id"
data-autocomplete-items-property="items"
data-autocomplete-filter-param-name="filter">
<select id="AuthorSelect" asp-for="@Model.AuthorId"
class="auto-complete-select"
data-placeholder="@L["SelectAnAuthor"]"
data-allow-clear="true"
data-autocomplete-api-url="/api/cms-kit-public/blog-posts/authors"
data-autocomplete-display-property="userName"
data-autocomplete-value-property="id"
data-autocomplete-items-property="items"
data-autocomplete-filter-param-name="filter">
@if (Model.SelectedAuthor != null)
{
<option selected value="@Model.AuthorId" selected="selected">@Model.SelectedAuthor.UserName</option>
}
</select>
</div>
</abp-column>
</abp-row>
<hr />
}
@if (Model.TagId.HasValue)
{
<h4>@L["Tags"]</h4>
<span class="badge rounded-pill badge-secondary text-bg-secondary">@Model.FilteredTagName
<a href="/@CmsBlogsWebConsts.BlogsRoutePrefix/@Model.BlogSlug" class="text-bg-secondary"><i class="fa fa-close ms-1"></i></a>
</span>
<hr />
}
@if(Model.SelectedAuthor != null)
{
<option selected value="@Model.AuthorId" selected="selected">@Model.SelectedAuthor.UserName</option>
}
</select>
</div>
</abp-column>
</abp-row>
<abp-row id="blogs-container">
@foreach (var blog in Model.Blogs.Items)
{
@ -51,11 +66,11 @@
<abp-card>
@if (blog.CoverImageMediaId != null)
{
<img src="/api/cms-kit/media/@blog.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'"/>
<img src="/api/cms-kit/media/@blog.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
}
else
{
<img src="@(dummyImageSource)?text=@blog.Title" class="card-img-top"/>
<img src="@(dummyImageSource)?text=@blog.Title" class="card-img-top" />
}
<abp-card-body class="p-4">
<h5>@blog.Title</h5>
@ -65,7 +80,7 @@
</p>
<p style="min-height: 60px;">@blog.ShortDescription</p>
<div class="d-grid gap-2">
<a href="/blogs/@Model.BlogSlug/@blog.Slug" class="btn btn-light">
<a href="/@CmsBlogsWebConsts.BlogsRoutePrefix/@Model.BlogSlug/@blog.Slug" class="btn btn-light">
@L["Read"]
</a>
</div>
@ -76,6 +91,6 @@
</abp-row>
<abp-row>
<abp-column>
<abp-paginator model="Model.PagerModel"/>
<abp-paginator model="Model.PagerModel" />
</abp-column>
</abp-row>

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

@ -31,6 +31,8 @@ public class IndexModel : CmsKitPublicPageModelBase
public CmsUserDto SelectedAuthor { get; protected set; }
public string FilteredTagName { get; protected set; }
protected IBlogPostPublicAppService BlogPostPublicAppService { get; }
public IndexModel(IBlogPostPublicAppService blogPostPublicAppService)
@ -54,7 +56,12 @@ public class IndexModel : CmsKitPublicPageModelBase
{
SelectedAuthor = await BlogPostPublicAppService.GetAuthorHasBlogPostAsync(AuthorId.Value);
}
if (TagId is not null)
{
FilteredTagName = await BlogPostPublicAppService.GetTagNameAsync(TagId.Value);
}
return Page();
}
}

14
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/bootstrap-toc.css

@ -20,15 +20,17 @@ nav[data-toggle='toc'] .nav > li > a:focus {
text-decoration: none;
background-color: transparent;
border-left: 1px solid #563d7c;
border-radius: 0;
}
nav[data-toggle='toc'] .nav-link.active,
nav[data-toggle='toc'] .nav-link.active:hover,
nav[data-toggle='toc'] .nav-link.active:focus {
padding-left: 18px;
padding-left: 8px;
font-weight: bold;
color: #563d7c;
background-color: transparent;
border-left: 2px solid #563d7c;
border-radius: 0;
}
/* Nav: second level (shown on .active) */
@ -40,18 +42,16 @@ nav[data-toggle='toc'] .nav-link + ul {
nav[data-toggle='toc'] .nav .nav > li > a {
padding-top: 1px;
padding-bottom: 1px;
padding-left: 30px;
padding-left: 8px;
margin-left: 8px;
font-size: 12px;
font-weight: normal;
}
nav[data-toggle='toc'] .nav .nav > li > a:hover,
nav[data-toggle='toc'] .nav .nav > li > a:focus {
padding-left: 29px;
}
nav[data-toggle='toc'] .nav .nav > li > .active,
nav[data-toggle='toc'] .nav .nav > li > .active:hover,
nav[data-toggle='toc'] .nav .nav > li > .active:focus {
padding-left: 28px;
padding-left: 8px;
font-weight: 500;
}

15
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostPublicAppService_Tests.cs

@ -179,4 +179,19 @@ public class BlogPostPublicAppService_Tests : CmsKitApplicationTestBase
allItemsFromRepository.Any(x => x.Id == draftBlogPost2.Id).ShouldBeTrue();
allItemsFromRepository.Any(x => x.Id == publishedBlogPost1.Id).ShouldBeTrue();
}
[Fact]
public async Task GetTagNameAsync_ShouldReturnCorrectName()
{
var tagName = await blogPostAppService.GetTagNameAsync(cmsKitTestData.TagId_1);
tagName.ShouldBe(cmsKitTestData.TagName_1);
}
[Fact]
public async Task GetTagNameAsync_WithNonExistingIdShouldReturnCorrectName()
{
await Should.ThrowAsync<EntityNotFoundException>(
blogPostAppService.GetTagNameAsync(Guid.NewGuid()));
}
}

Loading…
Cancel
Save