Browse Source

merge from dev

pull/12087/head
enisn 5 years ago
parent
commit
ec47abd3e0
  1. 2
      README.md
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  4. 2
      docs/en/AspNet-Boilerplate-Migration-Guide.md
  5. 30
      docs/en/Distributed-Locking.md
  6. 2
      docs/en/UI/Angular/Theming.md
  7. 14
      framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs
  8. 3
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs
  9. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
  10. 1
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  11. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs
  12. 4
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  13. 15
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs
  14. 4
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  15. 11
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqConsts.cs
  16. 56
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs
  17. 12
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  18. 16
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs
  19. 20
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi.Client/ClientProxies/cms-kit-admin-generate-proxy.json
  20. 18
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Update.cshtml
  21. 14
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/update.js
  22. 17
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/BlogPostScrollIndexFeature.cs
  23. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs
  24. 8
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  25. 8
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  26. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs
  27. 29
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs
  28. 31
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs
  29. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostFilteredPagedAndSortedResultRequestDto.cs
  30. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs
  31. 19
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  32. 18
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs
  33. 27
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.Generated.cs
  34. 7
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.cs
  35. 190
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  36. 11
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs
  37. 197
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  38. 30
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml
  39. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml.cs
  40. 17
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css
  41. 46
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogpost-scroll-index.js
  42. 60
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/bootstrap-toc.css
  43. 168
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/bootstrap-toc.js
  44. 22
      modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js
  45. 2
      modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs
  46. 2
      npm/ng-packs/packages/core/src/lib/models/environment.ts

2
README.md

@ -110,7 +110,7 @@ Love ABP Framework? **Please give a star** to this repository :star:
## Discord Channel ## Discord Channel
You can use this link to join the ABP Community Discord Server: https://discord.gg/uVGt6hyhcm You can use this link to join the ABP Community Discord Server: https://discord.gg/abp
## ABP Commercial ## ABP Commercial

3
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -380,6 +380,7 @@
"PurchaseItems": "Purchase Items", "PurchaseItems": "Purchase Items",
"SuccessfullyUpdated": "Successfully updated", "SuccessfullyUpdated": "Successfully updated",
"SuccessfullyAdded": "Successfully added", "SuccessfullyAdded": "Successfully added",
"PurchaseState": "Purchase State" "PurchaseState": "Purchase State",
"ShowBetweenDayCount": "Show Between Days"
} }
} }

7
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json

@ -495,7 +495,9 @@
"LicenseTypeNotCorrect": "The license type is not correct!", "LicenseTypeNotCorrect": "The license type is not correct!",
"Trainings": "Trainings", "Trainings": "Trainings",
"ChoseTrainingPlaceholder": "Chose the training...", "ChoseTrainingPlaceholder": "Chose the training...",
"ContactUsToGetQuote": "Contact us to get a quote", "DoYouNeedTrainings": "Do you need one of these trainings?",
"DoYouNeedTraining": "Do you need {0} training?",
"GetInTouchUs": "Get in touch with us",
"ForMoreInformationClickHere": "For more information, click <a href='{0}'>here.</a>", "ForMoreInformationClickHere": "For more information, click <a href='{0}'>here.</a>",
"IsGetOnboardingTraining": "Would you like to get onboarding & web application development training?", "IsGetOnboardingTraining": "Would you like to get onboarding & web application development training?",
"OnboardingWebApplicationDevelopmentTrainingMessage": "To schedule your training calendar, please contact {0} after creating the organization", "OnboardingWebApplicationDevelopmentTrainingMessage": "To schedule your training calendar, please contact {0} after creating the organization",
@ -503,6 +505,7 @@
"AdditionalNote": "Additional Note", "AdditionalNote": "Additional Note",
"OnboardingTrainingFaqTitle": "Do you have ABP onboarding training?", "OnboardingTrainingFaqTitle": "Do you have ABP onboarding training?",
"OnboardingTrainingFaqExplanation": "Yes, we have ABP Training Services to help you get your ABP project started fast. You will learn about ABP from an ABP core team member and you will get the skills to begin your ABP project. In the onboarding training, we will explain how to set up your development environment, install the required tools, create a fully functional CRUD page. The training will be live and the Zoom application will be used, and we are open to using other online meeting platforms. The language of the training will be English. You can also ask your questions about ABP during the sessions. A convenient time and date will be planned for both parties. To get more information, contact us at <a href=\"mailto:info@abp.io\">info@abp.io</a>.", "OnboardingTrainingFaqExplanation": "Yes, we have ABP Training Services to help you get your ABP project started fast. You will learn about ABP from an ABP core team member and you will get the skills to begin your ABP project. In the onboarding training, we will explain how to set up your development environment, install the required tools, create a fully functional CRUD page. The training will be live and the Zoom application will be used, and we are open to using other online meeting platforms. The language of the training will be English. You can also ask your questions about ABP during the sessions. A convenient time and date will be planned for both parties. To get more information, contact us at <a href=\"mailto:info@abp.io\">info@abp.io</a>.",
"AddBasket": "Add to Basket" "AddBasket": "Add to Basket",
"SendTrainingRequest": "Send Training Request"
} }
} }

2
docs/en/AspNet-Boilerplate-Migration-Guide.md

@ -1,4 +1,4 @@
# ASP.NET Boilerplate v5+ to ABP Framework Migration # Migrating from ASP.NET Boilerplate to the ABP Framework
ABP Framework is **the successor** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. This guide aims to help you to **migrate your existing solutions** (you developed with the ASP.NET Boilerplate framework) to the ABP Framework. ABP Framework is **the successor** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. This guide aims to help you to **migrate your existing solutions** (you developed with the ASP.NET Boilerplate framework) to the ABP Framework.

30
docs/en/Distributed-Locking.md

@ -27,21 +27,25 @@ using Medallion.Threading.Redis;
namespace AbpDemo namespace AbpDemo
{ {
public class MyModule : AbpModule [DependsOn(
{ typeof(AbpDistributedLockingModule)
public override void ConfigureServices(ServiceConfigurationContext context) //If you have the other dependencies, you should do here
{ )]
var configuration = context.Services.GetConfiguration(); public class MyModule : AbpModule
{
context.Services.AddSingleton<IDistributedLockProvider>(sp => public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var connection = ConnectionMultiplexer var configuration = context.Services.GetConfiguration();
context.Services.AddSingleton<IDistributedLockProvider>(sp =>
{
var connection = ConnectionMultiplexer
.Connect(configuration["Redis:Configuration"]); .Connect(configuration["Redis:Configuration"]);
return new return new
RedisDistributedSynchronizationProvider(connection.GetDatabase()); RedisDistributedSynchronizationProvider(connection.GetDatabase());
}); });
} }
} }
} }
```` ````

2
docs/en/UI/Angular/Theming.md

@ -227,7 +227,7 @@ import { Router } from '@angular/router';
template: ` template: `
<a class="dropdown-item pointer" (click)="data.action()"> <a class="dropdown-item pointer" (click)="data.action()">
<i *ngIf="data.textTemplate.icon" [class]="data.textTemplate.icon"></i> <i *ngIf="data.textTemplate.icon" [class]="data.textTemplate.icon"></i>
{{ data.textTemplate.text | abpLocalization }} {%{{{ data.textTemplate.text | abpLocalization }}}%}
</a> </a>
`, `,
}) })

14
framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Microsoft/AspNetCore/Authentication/OAuth/Claims/AbpClaimActionCollectionExtensions.cs

@ -13,6 +13,20 @@ public static class AbpClaimActionCollectionExtensions
claimActions.DeleteClaim("name"); claimActions.DeleteClaim("name");
claimActions.RemoveDuplicate(AbpClaimTypes.UserName); claimActions.RemoveDuplicate(AbpClaimTypes.UserName);
} }
if (AbpClaimTypes.Name != "given_name")
{
claimActions.MapJsonKey(AbpClaimTypes.Name, "given_name");
claimActions.DeleteClaim("given_name");
claimActions.RemoveDuplicate(AbpClaimTypes.Name);
}
if (AbpClaimTypes.SurName != "family_name")
{
claimActions.MapJsonKey(AbpClaimTypes.SurName, "family_name");
claimActions.DeleteClaim("family_name");
claimActions.RemoveDuplicate(AbpClaimTypes.SurName);
}
if (AbpClaimTypes.Email != "email") if (AbpClaimTypes.Email != "email")
{ {

3
framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs

@ -57,8 +57,7 @@ public static class AbpOpenIdConnectExtensions
if (receivedContext.Request.Cookies.ContainsKey(tenantKey)) if (receivedContext.Request.Cookies.ContainsKey(tenantKey))
{ {
receivedContext.TokenEndpointRequest.SetParameter(tenantKey, receivedContext.TokenEndpointRequest?.SetParameter(tenantKey, receivedContext.Request.Cookies[tenantKey]);
receivedContext.Request.Cookies[tenantKey]);
} }
} }
} }

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js

@ -431,9 +431,7 @@ var abp = abp || {};
configuration.language = datatables.defaultConfigurations.language(); configuration.language = datatables.defaultConfigurations.language();
if(configuration.dom){ if(!configuration.dom){
configuration.dom += datatables.defaultConfigurations.dom;
}else{
configuration.dom = datatables.defaultConfigurations.dom; configuration.dom = datatables.defaultConfigurations.dom;
} }

1
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -82,6 +82,7 @@ public class AbpExceptionHandlingMiddleware : IMiddleware, ITransientDependency
httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception); httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response); httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); httpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
httpContext.Response.Headers.Add("Content-Type", "application/json");
await httpContext.Response.WriteAsync( await httpContext.Response.WriteAsync(
jsonSerializer.Serialize( jsonSerializer.Serialize(

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs

@ -81,12 +81,12 @@ public class DatabaseManagementSystemChangeStep : ProjectBuildPipelineStep
private void ChangeEntityFrameworkCoreDependency(ProjectBuildContext context, string newPackageName, string newModuleNamespace, string newModuleClass) private void ChangeEntityFrameworkCoreDependency(ProjectBuildContext context, string newPackageName, string newModuleNamespace, string newModuleClass)
{ {
var efCoreProjectFile = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCore.csproj", StringComparison.OrdinalIgnoreCase)); var efCoreProjectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("EntityFrameworkCore.csproj", StringComparison.OrdinalIgnoreCase));
efCoreProjectFile.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newPackageName); efCoreProjectFile?.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newPackageName);
var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase)); var efCoreModuleClass = context.Files.FirstOrDefault(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase));
efCoreModuleClass.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newModuleNamespace); efCoreModuleClass?.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newModuleNamespace);
efCoreModuleClass.ReplaceText("AbpEntityFrameworkCoreSqlServerModule", newModuleClass); efCoreModuleClass?.ReplaceText("AbpEntityFrameworkCoreSqlServerModule", newModuleClass);
} }
private void ChangeUseSqlServer(ProjectBuildContext context, string newUseMethodForEfModule, string newUseMethodForDbContext = null) private void ChangeUseSqlServer(ProjectBuildContext context, string newUseMethodForEfModule, string newUseMethodForDbContext = null)

4
framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs

@ -57,8 +57,6 @@ public static class CultureHelper
public static string GetBaseCultureName(string cultureName) public static string GetBaseCultureName(string cultureName)
{ {
return cultureName.Contains("-") return new CultureInfo(cultureName).Parent.Name;
? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal))
: cultureName;
} }
} }

15
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpRabbitMqEventBusOptions.cs

@ -1,10 +1,23 @@
namespace Volo.Abp.EventBus.RabbitMq; using Volo.Abp.RabbitMQ;
namespace Volo.Abp.EventBus.RabbitMq;
public class AbpRabbitMqEventBusOptions public class AbpRabbitMqEventBusOptions
{ {
public const string DefaultExchangeType = RabbitMqConsts.ExchangeTypes.Direct;
public string ConnectionName { get; set; } public string ConnectionName { get; set; }
public string ClientName { get; set; } public string ClientName { get; set; }
public string ExchangeName { get; set; } public string ExchangeName { get; set; }
public string ExchangeType { get; set; }
public string GetExchangeTypeOrDefault()
{
return string.IsNullOrEmpty(ExchangeType)
? DefaultExchangeType
: ExchangeType;
}
} }

4
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -69,7 +69,7 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, ISingletonDe
Consumer = MessageConsumerFactory.Create( Consumer = MessageConsumerFactory.Create(
new ExchangeDeclareConfiguration( new ExchangeDeclareConfiguration(
AbpRabbitMqEventBusOptions.ExchangeName, AbpRabbitMqEventBusOptions.ExchangeName,
type: "direct", type: AbpRabbitMqEventBusOptions.GetExchangeTypeOrDefault(),
durable: true durable: true
), ),
new QueueDeclareConfiguration( new QueueDeclareConfiguration(
@ -244,7 +244,7 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, ISingletonDe
{ {
channel.ExchangeDeclare( channel.ExchangeDeclare(
AbpRabbitMqEventBusOptions.ExchangeName, AbpRabbitMqEventBusOptions.ExchangeName,
"direct", AbpRabbitMqEventBusOptions.GetExchangeTypeOrDefault(),
durable: true durable: true
); );

11
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqConsts.cs

@ -8,4 +8,15 @@ public static class RabbitMqConsts
public const int Persistent = 2; public const int Persistent = 2;
} }
public static class ExchangeTypes
{
public const string Direct = "direct";
public const string Topic = "topic";
public const string Fanout = "fanout";
public const string Headers = "headers";
}
} }

56
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs

@ -1,4 +1,4 @@
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Shouldly; using Shouldly;
@ -183,6 +183,60 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
{ {
_localizer["CarPlural"].Value.ShouldBe("Autos"); _localizer["CarPlural"].Value.ShouldBe("Autos");
} }
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hans")))
{
_localizer["Car"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hans")))
{
_localizer["CarPlural"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-CN")))
{
_localizer["Car"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-CN")))
{
_localizer["CarPlural"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hans-CN")))
{
_localizer["Car"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hans-CN")))
{
_localizer["CarPlural"].Value.ShouldBe("汽车");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hant")))
{
_localizer["Car"].Value.ShouldBe("汽車");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hant")))
{
_localizer["CarPlural"].Value.ShouldBe("汽車");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-TW")))
{
_localizer["Car"].Value.ShouldBe("汽車");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-TW")))
{
_localizer["CarPlural"].Value.ShouldBe("汽車");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hant-TW")))
{
_localizer["Car"].Value.ShouldBe("汽車");
}
using (CultureHelper.Use(CultureInfo.GetCultureInfo("zh-Hant-TW")))
{
_localizer["CarPlural"].Value.ShouldBe("汽車");
}
} }
[Fact] [Fact]

12
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -277,6 +277,18 @@ public class LoginModel : AccountPageModel
CheckIdentityErrors(await UserManager.SetEmailAsync(user, emailAddress)); CheckIdentityErrors(await UserManager.SetEmailAsync(user, emailAddress));
CheckIdentityErrors(await UserManager.AddLoginAsync(user, info)); CheckIdentityErrors(await UserManager.AddLoginAsync(user, info));
CheckIdentityErrors(await UserManager.AddDefaultRolesAsync(user)); CheckIdentityErrors(await UserManager.AddDefaultRolesAsync(user));
user.Name = info.Principal.FindFirstValue(AbpClaimTypes.Name);
user.Surname = info.Principal.FindFirstValue(AbpClaimTypes.SurName);
var phoneNumber = info.Principal.FindFirstValue(AbpClaimTypes.PhoneNumber);
if (!phoneNumber.IsNullOrWhiteSpace())
{
var phoneNumberConfirmed = string.Equals(info.Principal.FindFirstValue(AbpClaimTypes.PhoneNumberVerified), "true", StringComparison.InvariantCultureIgnoreCase);
user.SetPhoneNumber(phoneNumber, phoneNumberConfirmed);
}
await UserManager.UpdateAsync(user);
return user; return user;
} }

16
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs

@ -6,6 +6,7 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.GlobalFeatures; using Volo.Abp.GlobalFeatures;
using Volo.Abp.Users; using Volo.Abp.Users;
using Volo.CmsKit.Admin.MediaDescriptors;
using Volo.CmsKit.Blogs; using Volo.CmsKit.Blogs;
using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Permissions; using Volo.CmsKit.Permissions;
@ -22,16 +23,20 @@ public class BlogPostAdminAppService : CmsKitAppServiceBase, IBlogPostAdminAppSe
protected IBlogRepository BlogRepository { get; } protected IBlogRepository BlogRepository { get; }
protected ICmsUserLookupService UserLookupService { get; } protected ICmsUserLookupService UserLookupService { get; }
protected IMediaDescriptorAdminAppService MediaDescriptorAdminAppService { get; }
public BlogPostAdminAppService( public BlogPostAdminAppService(
BlogPostManager blogPostManager, BlogPostManager blogPostManager,
IBlogPostRepository blogPostRepository, IBlogPostRepository blogPostRepository,
IBlogRepository blogRepository, IBlogRepository blogRepository,
ICmsUserLookupService userLookupService) ICmsUserLookupService userLookupService,
IMediaDescriptorAdminAppService mediaDescriptorAdminAppService)
{ {
BlogPostManager = blogPostManager; BlogPostManager = blogPostManager;
BlogPostRepository = blogPostRepository; BlogPostRepository = blogPostRepository;
BlogRepository = blogRepository; BlogRepository = blogRepository;
UserLookupService = userLookupService; UserLookupService = userLookupService;
MediaDescriptorAdminAppService = mediaDescriptorAdminAppService;
} }
[Authorize(CmsKitAdminPermissions.BlogPosts.Create)] [Authorize(CmsKitAdminPermissions.BlogPosts.Create)]
@ -61,13 +66,18 @@ public class BlogPostAdminAppService : CmsKitAppServiceBase, IBlogPostAdminAppSe
public virtual async Task<BlogPostDto> UpdateAsync(Guid id, UpdateBlogPostDto input) public virtual async Task<BlogPostDto> UpdateAsync(Guid id, UpdateBlogPostDto input)
{ {
var blogPost = await BlogPostRepository.GetAsync(id); var blogPost = await BlogPostRepository.GetAsync(id);
blogPost.SetTitle(input.Title); blogPost.SetTitle(input.Title);
blogPost.SetShortDescription(input.ShortDescription); blogPost.SetShortDescription(input.ShortDescription);
blogPost.SetContent(input.Content); blogPost.SetContent(input.Content);
blogPost.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp); blogPost.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
if (blogPost.CoverImageMediaId != null && input.CoverImageMediaId == null)
{
await MediaDescriptorAdminAppService.DeleteAsync(blogPost.CoverImageMediaId.Value);
}
blogPost.CoverImageMediaId = input.CoverImageMediaId; blogPost.CoverImageMediaId = input.CoverImageMediaId;
if (blogPost.Slug != input.Slug) if (blogPost.Slug != input.Slug)
{ {
await BlogPostManager.SetSlugUrlAsync(blogPost, input.Slug); await BlogPostManager.SetSlugUrlAsync(blogPost, input.Slug);

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

@ -7,6 +7,8 @@
"Volo.CmsKit.Admin.Tags.EntityTagAdminController": { "Volo.CmsKit.Admin.Tags.EntityTagAdminController": {
"controllerName": "EntityTagAdmin", "controllerName": "EntityTagAdmin",
"controllerGroupName": "EntityTagAdmin", "controllerGroupName": "EntityTagAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Tags.EntityTagAdminController", "type": "Volo.CmsKit.Admin.Tags.EntityTagAdminController",
"interfaces": [ "interfaces": [
{ {
@ -154,6 +156,8 @@
"Volo.CmsKit.Admin.Tags.TagAdminController": { "Volo.CmsKit.Admin.Tags.TagAdminController": {
"controllerName": "TagAdmin", "controllerName": "TagAdmin",
"controllerGroupName": "TagAdmin", "controllerGroupName": "TagAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Tags.TagAdminController", "type": "Volo.CmsKit.Admin.Tags.TagAdminController",
"interfaces": [ "interfaces": [
{ {
@ -422,6 +426,8 @@
"Volo.CmsKit.Admin.Pages.PageAdminController": { "Volo.CmsKit.Admin.Pages.PageAdminController": {
"controllerName": "PageAdmin", "controllerName": "PageAdmin",
"controllerGroupName": "PageAdmin", "controllerGroupName": "PageAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Pages.PageAdminController", "type": "Volo.CmsKit.Admin.Pages.PageAdminController",
"interfaces": [ "interfaces": [
{ {
@ -675,6 +681,8 @@
"Volo.CmsKit.Admin.Menus.MenuItemAdminController": { "Volo.CmsKit.Admin.Menus.MenuItemAdminController": {
"controllerName": "MenuItemAdmin", "controllerName": "MenuItemAdmin",
"controllerGroupName": "MenuItemAdmin", "controllerGroupName": "MenuItemAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Menus.MenuItemAdminController", "type": "Volo.CmsKit.Admin.Menus.MenuItemAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1000,6 +1008,8 @@
"Volo.CmsKit.Admin.MediaDescriptors.MediaDescriptorAdminController": { "Volo.CmsKit.Admin.MediaDescriptors.MediaDescriptorAdminController": {
"controllerName": "MediaDescriptorAdmin", "controllerName": "MediaDescriptorAdmin",
"controllerGroupName": "MediaDescriptorAdmin", "controllerGroupName": "MediaDescriptorAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.MediaDescriptors.MediaDescriptorAdminController", "type": "Volo.CmsKit.Admin.MediaDescriptors.MediaDescriptorAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1118,6 +1128,8 @@
"Volo.CmsKit.Admin.GlobalResources.GlobalResourceAdminController": { "Volo.CmsKit.Admin.GlobalResources.GlobalResourceAdminController": {
"controllerName": "GlobalResourceAdmin", "controllerName": "GlobalResourceAdmin",
"controllerGroupName": "GlobalResourceAdmin", "controllerGroupName": "GlobalResourceAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.GlobalResources.GlobalResourceAdminController", "type": "Volo.CmsKit.Admin.GlobalResources.GlobalResourceAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1182,6 +1194,8 @@
"Volo.CmsKit.Admin.Comments.CommentAdminController": { "Volo.CmsKit.Admin.Comments.CommentAdminController": {
"controllerName": "CommentAdmin", "controllerName": "CommentAdmin",
"controllerGroupName": "CommentAdmin", "controllerGroupName": "CommentAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Comments.CommentAdminController", "type": "Volo.CmsKit.Admin.Comments.CommentAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1401,6 +1415,8 @@
"Volo.CmsKit.Admin.Blogs.BlogAdminController": { "Volo.CmsKit.Admin.Blogs.BlogAdminController": {
"controllerName": "BlogAdmin", "controllerName": "BlogAdmin",
"controllerGroupName": "BlogAdmin", "controllerGroupName": "BlogAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Blogs.BlogAdminController", "type": "Volo.CmsKit.Admin.Blogs.BlogAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1654,6 +1670,8 @@
"Volo.CmsKit.Admin.Blogs.BlogFeatureAdminController": { "Volo.CmsKit.Admin.Blogs.BlogFeatureAdminController": {
"controllerName": "BlogFeatureAdmin", "controllerName": "BlogFeatureAdmin",
"controllerGroupName": "BlogFeatureAdmin", "controllerGroupName": "BlogFeatureAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Blogs.BlogFeatureAdminController", "type": "Volo.CmsKit.Admin.Blogs.BlogFeatureAdminController",
"interfaces": [ "interfaces": [
{ {
@ -1760,6 +1778,8 @@
"Volo.CmsKit.Admin.Blogs.BlogPostAdminController": { "Volo.CmsKit.Admin.Blogs.BlogPostAdminController": {
"controllerName": "BlogPostAdmin", "controllerName": "BlogPostAdmin",
"controllerGroupName": "BlogPostAdmin", "controllerGroupName": "BlogPostAdmin",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Admin.Blogs.BlogPostAdminController", "type": "Volo.CmsKit.Admin.Blogs.BlogPostAdminController",
"interfaces": [ "interfaces": [
{ {

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

@ -48,13 +48,17 @@
<abp-card-body> <abp-card-body>
<div class="mb-3"> <div class="mb-3">
@if (Model.ViewModel.CoverImageMediaId != null) <div id="CurrentCoverImageArea">
{ @if (Model.ViewModel.CoverImageMediaId != null)
<img height="120" src="/api/cms-kit/media/@Model.ViewModel.CoverImageMediaId" /> {
<br /> <img height="120" src="/api/cms-kit/media/@Model.ViewModel.CoverImageMediaId"/>
} <br/>
<label class="form-label" >@L["CoverImage"]</label> <abp-button button-type="Link" type="button" text="@L["RemoveCoverImage"].Value" id="button-remove-cover-image"/>
<input type="file" id="BlogPostCoverImage" class="form-control" /> <br/>
}
</div>
<label class="form-label">@L["CoverImage"]</label>
<input type="file" id="BlogPostCoverImage" class="form-control"/>
</div> </div>
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/BlogPosts/Update" id="form-blog-post-update"> <abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/BlogPosts/Update" id="form-blog-post-update">

14
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/update.js

@ -10,6 +10,7 @@ $(function () {
var $blogPostIdInput = $('#Id'); var $blogPostIdInput = $('#Id');
var $tagsInput = $('.tag-editor-form input[name=tags]'); var $tagsInput = $('.tag-editor-form input[name=tags]');
var $fileInput = $('#BlogPostCoverImage'); var $fileInput = $('#BlogPostCoverImage');
var $buttonRemoveCoverImage = $('#button-remove-cover-image');
var UPPY_FILE_ID = "uppy-upload-file"; var UPPY_FILE_ID = "uppy-upload-file";
@ -152,7 +153,6 @@ $(function () {
} }
} }
// ----------------------------------- // -----------------------------------
var fileUploadUri = "/api/cms-kit-admin/media/blogpost"; var fileUploadUri = "/api/cms-kit-admin/media/blogpost";
var fileUriPrefix = "/api/cms-kit/media/"; var fileUriPrefix = "/api/cms-kit/media/";
@ -225,4 +225,16 @@ $(function () {
} }
}); });
} }
$buttonRemoveCoverImage.on('click', function () {
abp.message.confirm(
l('RemoveCoverImageConfirmationMessage'),
function (isConfirmed) {
if (isConfirmed) {
$coverImage.val(null);
$('#CurrentCoverImageArea').remove();
}
}
);
});
}); });

17
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/BlogPostScrollIndexFeature.cs

@ -0,0 +1,17 @@
using JetBrains.Annotations;
using Volo.Abp.GlobalFeatures;
namespace Volo.CmsKit.GlobalFeatures;
[GlobalFeatureName(Name)]
public class BlogPostScrollIndexFeature : GlobalFeature
{
public const string Name = "CmsKit.BlogPost.ScrollIndex";
internal BlogPostScrollIndexFeature(
[NotNull] GlobalCmsKitFeatures cmsKit
) : base(cmsKit)
{
}
}

3
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs

@ -26,6 +26,8 @@ public class GlobalCmsKitFeatures : GlobalModuleFeatures
public MenuFeature Menu => GetFeature<MenuFeature>(); public MenuFeature Menu => GetFeature<MenuFeature>();
public GlobalResourcesFeature GlobalResources => GetFeature<GlobalResourcesFeature>(); public GlobalResourcesFeature GlobalResources => GetFeature<GlobalResourcesFeature>();
public BlogPostScrollIndexFeature BlogPostScrollIndex => GetFeature<BlogPostScrollIndexFeature>();
public GlobalCmsKitFeatures([NotNull] GlobalFeatureManager featureManager) public GlobalCmsKitFeatures([NotNull] GlobalFeatureManager featureManager)
: base(featureManager) : base(featureManager)
@ -40,5 +42,6 @@ public class GlobalCmsKitFeatures : GlobalModuleFeatures
AddFeature(new CmsUserFeature(this)); AddFeature(new CmsUserFeature(this));
AddFeature(new MenuFeature(this)); AddFeature(new MenuFeature(this));
AddFeature(new GlobalResourcesFeature(this)); AddFeature(new GlobalResourcesFeature(this));
AddFeature(new BlogPostScrollIndexFeature(this));
} }
} }

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

@ -131,6 +131,7 @@
"SelectAll": "Select All", "SelectAll": "Select All",
"Send": "Send", "Send": "Send",
"SendMessage": "Send Message", "SendMessage": "Send Message",
"SelectedAuthor": "Author",
"ShortDescription": "Short description", "ShortDescription": "Short description",
"Slug": "Slug", "Slug": "Slug",
"Source": "Source", "Source": "Source",
@ -182,6 +183,11 @@
"HasBlogPostWaitingForReviewMessage": "You have a blog post waiting for review. Click to list.", "HasBlogPostWaitingForReviewMessage": "You have a blog post waiting for review. Click to list.",
"SelectAStatus": "Select a status", "SelectAStatus": "Select a status",
"Status": "Status", "Status": "Status",
"SelectAnAuthor": "Select an author" "SelectAnAuthor": "Select an author",
"SavedSuccessfully": "Saved successfully",
"GoToTop": "Go to top",
"InThisDocument": "In this document",
"RemoveCoverImageConfirmationMessage": "Are you sure to remove the cover image?",
"RemoveCoverImage": "Remove the cover image"
} }
} }

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

@ -130,6 +130,7 @@
"SelectAll": "Hepsini seç", "SelectAll": "Hepsini seç",
"Send": "Gönder", "Send": "Gönder",
"SendMessage": "Mesajı Gönder", "SendMessage": "Mesajı Gönder",
"SelectedAuthor": "Yazar",
"ShortDescription": "Kısa açıklama", "ShortDescription": "Kısa açıklama",
"Slug": "Etiket", "Slug": "Etiket",
"Source": "Kaynak", "Source": "Kaynak",
@ -181,6 +182,11 @@
"HasBlogPostWaitingForReviewMessage": "Yayınlanmak için onay bekleyen blog postlar var! Listelemek için tıklayın.", "HasBlogPostWaitingForReviewMessage": "Yayınlanmak için onay bekleyen blog postlar var! Listelemek için tıklayın.",
"SelectAStatus": "Durum seçin", "SelectAStatus": "Durum seçin",
"Status": "Durum", "Status": "Durum",
"SelectAnAuthor": "Bir yazar seçin" "SelectAnAuthor": "Bir yazar seçin",
"SavedSuccessfully": "Başarıyla kaydedildi",
"GoToTop": "Yukarı Git",
"InThisDocument": "Bu belgede",
"RemoveCoverImageConfirmationMessage": "Kapak resmini kaldırmak istediğinize emin misiniz?",
"RemoveCoverImage": "Kapak resmini kaldır"
} }
} }

11
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs

@ -30,7 +30,16 @@ public interface IBlogPostRepository : IBasicRepository<BlogPost, Guid>
Task<BlogPost> GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default); Task<BlogPost> GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default);
Task<List<CmsUser>> GetAuthorsHasBlogPosts(CancellationToken cancellationToken = default); Task<List<CmsUser>> GetAuthorsHasBlogPostsAsync(
int skipCount,
int maxResultCount,
string sorting,
string filter,
CancellationToken cancellationToken = default);
Task<int> GetAuthorsHasBlogPostsCountAsync(string filter, CancellationToken cancellationToken = default);
Task<CmsUser> GetAuthorHasBlogPostAsync(Guid id, CancellationToken cancellationToken = default);
Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default); Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default);
} }

29
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs

@ -7,6 +7,7 @@ using System.Linq.Dynamic.Core;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore; using Volo.CmsKit.EntityFrameworkCore;
@ -104,13 +105,33 @@ public class EfCoreBlogPostRepository : EfCoreRepository<CmsKitDbContext, BlogPo
GetCancellationToken(cancellationToken)); GetCancellationToken(cancellationToken));
} }
public async Task<List<CmsUser>> GetAuthorsHasBlogPosts(CancellationToken cancellationToken = default) public async Task<List<CmsUser>> GetAuthorsHasBlogPostsAsync(int skipCount, int maxResultCount, string sorting, string filter, CancellationToken cancellationToken = default)
{ {
return await (await GetDbContextAsync()).BlogPosts return await (await CreateAuthorsQueryableAsync())
.Where(x => x.Status == BlogPostStatus.Published) .Skip(skipCount)
.Select(x => x.Author).Distinct() .Take(maxResultCount)
.WhereIf(!filter.IsNullOrEmpty(), x => x.UserName.Contains(filter.ToLower()))
.OrderBy(sorting.IsNullOrEmpty() ? nameof(CmsUser.UserName) : sorting)
.ToListAsync(GetCancellationToken(cancellationToken)); .ToListAsync(GetCancellationToken(cancellationToken));
} }
public async Task<int> GetAuthorsHasBlogPostsCountAsync(string filter, CancellationToken cancellationToken = default)
{
return await (await CreateAuthorsQueryableAsync())
.WhereIf(!filter.IsNullOrEmpty(), x => x.UserName.Contains(filter.ToLower()))
.CountAsync(GetCancellationToken(cancellationToken));
}
public async Task<CmsUser> GetAuthorHasBlogPostAsync(Guid id, CancellationToken cancellationToken = default)
{
return await (await CreateAuthorsQueryableAsync()).FirstOrDefaultAsync(x => x.Id == id, GetCancellationToken(cancellationToken))
?? throw new EntityNotFoundException(typeof(CmsUser), id);
}
private async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync()
{
return (await GetDbContextAsync()).BlogPosts.Select(x => x.Author).Distinct();
}
public virtual async Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default) public virtual async Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default)
{ {

31
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs

@ -8,6 +8,7 @@ using System.Linq.Dynamic.Core;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
using Volo.CmsKit.Blogs; using Volo.CmsKit.Blogs;
@ -108,7 +109,31 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
return await queryable.AnyAsync(x => x.BlogId == blogId && x.Slug.ToLower() == slug, cancellationToken); return await queryable.AnyAsync(x => x.BlogId == blogId && x.Slug.ToLower() == slug, cancellationToken);
} }
public async Task<List<CmsUser>> GetAuthorsHasBlogPosts(CancellationToken cancellationToken = default) public async Task<List<CmsUser>> GetAuthorsHasBlogPostsAsync(int skipCount, int maxResultCount, string sorting, string filter, CancellationToken cancellationToken = default)
{
var queryable = (await CreateAuthorsQueryableAsync())
.Skip(skipCount)
.Take(maxResultCount)
.OrderBy(sorting.IsNullOrEmpty() ? nameof(CmsUser.UserName) : sorting)
.WhereIf(!filter.IsNullOrEmpty(), x => x.UserName.Contains(filter.ToLower()));
return await AsyncExecuter.ToListAsync(queryable, GetCancellationToken(cancellationToken));
}
public async Task<int> GetAuthorsHasBlogPostsCountAsync(string filter, CancellationToken cancellationToken = default)
{
return await AsyncExecuter.CountAsync(
(await CreateAuthorsQueryableAsync())
.WhereIf(!filter.IsNullOrEmpty(), x => x.UserName.Contains(filter.ToLower())));
}
public async Task<CmsUser> GetAuthorHasBlogPostAsync(Guid id, CancellationToken cancellationToken = default)
{
return await AsyncExecuter.FirstOrDefaultAsync(await CreateAuthorsQueryableAsync(), x => x.Id == id)
?? throw new EntityNotFoundException(typeof(CmsUser), id);
}
private async Task<IQueryable<CmsUser>> CreateAuthorsQueryableAsync()
{ {
cancellationToken = GetCancellationToken(cancellationToken); cancellationToken = GetCancellationToken(cancellationToken);
@ -117,7 +142,7 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
var usersQueryable = (await GetDbContextAsync(cancellationToken)).Collection<CmsUser>().AsQueryable(); var usersQueryable = (await GetDbContextAsync(cancellationToken)).Collection<CmsUser>().AsQueryable();
var queryable = blogPostQueryable return blogPostQueryable
.Join( .Join(
usersQueryable, usersQueryable,
o => o.AuthorId, o => o.AuthorId,
@ -125,8 +150,6 @@ public class MongoBlogPostRepository : MongoDbRepository<CmsKitMongoDbContext, B
(blogPost, user) => new { blogPost, user }) (blogPost, user) => new { blogPost, user })
.Select(s => s.user) .Select(s => s.user)
.Distinct(); .Distinct();
return await AsyncExecuter.ToListAsync(queryable, cancellationToken);
} }
public virtual async Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default) public virtual async Task<bool> HasBlogPostWaitingForReviewAsync(CancellationToken cancellationToken = default)

8
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostFilteredPagedAndSortedResultRequestDto.cs

@ -0,0 +1,8 @@
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Public.Blogs;
public class BlogPostFilteredPagedAndSortedResultRequestDto : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
}

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

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.CmsKit.Users; using Volo.CmsKit.Users;
using System;
namespace Volo.CmsKit.Public.Blogs; namespace Volo.CmsKit.Public.Blogs;
@ -12,6 +13,8 @@ public interface IBlogPostPublicAppService : IApplicationService
Task<PagedResultDto<BlogPostPublicDto>> GetListAsync([NotNull] string blogSlug, BlogPostGetListInput input); Task<PagedResultDto<BlogPostPublicDto>> GetListAsync([NotNull] string blogSlug, BlogPostGetListInput input);
Task<BlogPostPublicDto> GetAsync([NotNull] string blogSlug, [NotNull] string blogPostSlug); Task<BlogPostPublicDto> GetAsync([NotNull] string blogSlug, [NotNull] string blogPostSlug);
Task<List<CmsUserDto>> GetAuthorsHasBlogPostsAsync(); Task<PagedResultDto<CmsUserDto>> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input);
Task<CmsUserDto> GetAuthorHasBlogPostAsync(Guid id);
} }

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

@ -1,4 +1,5 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
@ -15,7 +16,6 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
protected IBlogRepository BlogRepository { get; } protected IBlogRepository BlogRepository { get; }
protected IBlogPostRepository BlogPostRepository { get; } protected IBlogPostRepository BlogPostRepository { get; }
public BlogPostPublicAppService( public BlogPostPublicAppService(
IBlogRepository blogRepository, IBlogRepository blogRepository,
@ -46,9 +46,20 @@ public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPub
ObjectMapper.Map<List<BlogPost>, List<BlogPostPublicDto>>(blogPosts)); ObjectMapper.Map<List<BlogPost>, List<BlogPostPublicDto>>(blogPosts));
} }
public virtual async Task<List<CmsUserDto>> GetAuthorsHasBlogPostsAsync() public virtual async Task<PagedResultDto<CmsUserDto>> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input)
{ {
var authors = await BlogPostRepository.GetAuthorsHasBlogPosts(); var authors = await BlogPostRepository.GetAuthorsHasBlogPostsAsync(input.SkipCount, input.MaxResultCount, input.Sorting, input.Filter);
return ObjectMapper.Map<List<CmsUser>, List<CmsUserDto>>(authors); var authorDtos = ObjectMapper.Map<List<CmsUser>, List<CmsUserDto>>(authors);
return new PagedResultDto<CmsUserDto>(
await BlogPostRepository.GetAuthorsHasBlogPostsCountAsync(input.Filter),
authorDtos);
}
public async Task<CmsUserDto> GetAuthorHasBlogPostAsync(Guid id)
{
var author = await BlogPostRepository.GetAuthorHasBlogPostAsync(id);
return ObjectMapper.Map<CmsUser, CmsUserDto>(author);
} }
} }

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

@ -1,6 +1,5 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp // This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;
@ -25,7 +24,7 @@ public partial class BlogPostPublicClientProxy : ClientProxyBase<IBlogPostPublic
{ typeof(string), blogPostSlug } { typeof(string), blogPostSlug }
}); });
} }
public virtual async Task<PagedResultDto<BlogPostPublicDto>> GetListAsync(string blogSlug, BlogPostGetListInput input) public virtual async Task<PagedResultDto<BlogPostPublicDto>> GetListAsync(string blogSlug, BlogPostGetListInput input)
{ {
return await RequestAsync<PagedResultDto<BlogPostPublicDto>>(nameof(GetListAsync), new ClientProxyRequestTypeValue return await RequestAsync<PagedResultDto<BlogPostPublicDto>>(nameof(GetListAsync), new ClientProxyRequestTypeValue
@ -35,8 +34,19 @@ public partial class BlogPostPublicClientProxy : ClientProxyBase<IBlogPostPublic
}); });
} }
public virtual async Task<List<CmsUserDto>> GetAuthorsHasBlogPostsAsync() public virtual async Task<PagedResultDto<CmsUserDto>> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input)
{
return await RequestAsync<PagedResultDto<CmsUserDto>>(nameof(GetAuthorsHasBlogPostsAsync), new ClientProxyRequestTypeValue
{
{ typeof(BlogPostFilteredPagedAndSortedResultRequestDto), input }
});
}
public virtual async Task<CmsUserDto> GetAuthorHasBlogPostAsync(Guid id)
{ {
return await RequestAsync<List<CmsUserDto>>(nameof(GetAuthorsHasBlogPostsAsync)); return await RequestAsync<CmsUserDto>(nameof(GetAuthorHasBlogPostAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
} }
} }

27
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.Generated.cs

@ -0,0 +1,27 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Modeling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.CmsKit.Public.GlobalResources;
// ReSharper disable once CheckNamespace
namespace Volo.CmsKit.Public.GlobalResources.ClientProxies;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IGlobalResourcePublicAppService), typeof(GlobalResourcePublicClientProxy))]
public partial class GlobalResourcePublicClientProxy : ClientProxyBase<IGlobalResourcePublicAppService>, IGlobalResourcePublicAppService
{
public virtual async Task<GlobalResourceDto> GetGlobalScriptAsync()
{
return await RequestAsync<GlobalResourceDto>(nameof(GetGlobalScriptAsync));
}
public virtual async Task<GlobalResourceDto> GetGlobalStyleAsync()
{
return await RequestAsync<GlobalResourceDto>(nameof(GetGlobalStyleAsync));
}
}

7
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.cs

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

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

@ -7,6 +7,8 @@
"Volo.CmsKit.Public.Tags.TagPublicController": { "Volo.CmsKit.Public.Tags.TagPublicController": {
"controllerName": "TagPublic", "controllerName": "TagPublic",
"controllerGroupName": "TagPublic", "controllerGroupName": "TagPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Tags.TagPublicController", "type": "Volo.CmsKit.Public.Tags.TagPublicController",
"interfaces": [ "interfaces": [
{ {
@ -76,6 +78,8 @@
"Volo.CmsKit.Public.Reactions.ReactionPublicController": { "Volo.CmsKit.Public.Reactions.ReactionPublicController": {
"controllerName": "ReactionPublic", "controllerName": "ReactionPublic",
"controllerGroupName": "ReactionPublic", "controllerGroupName": "ReactionPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Reactions.ReactionPublicController", "type": "Volo.CmsKit.Public.Reactions.ReactionPublicController",
"interfaces": [ "interfaces": [
{ {
@ -299,6 +303,8 @@
"Volo.CmsKit.Public.Ratings.RatingPublicController": { "Volo.CmsKit.Public.Ratings.RatingPublicController": {
"controllerName": "RatingPublic", "controllerName": "RatingPublic",
"controllerGroupName": "RatingPublic", "controllerGroupName": "RatingPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Ratings.RatingPublicController", "type": "Volo.CmsKit.Public.Ratings.RatingPublicController",
"interfaces": [ "interfaces": [
{ {
@ -502,6 +508,8 @@
"Volo.CmsKit.Public.Pages.PagesPublicController": { "Volo.CmsKit.Public.Pages.PagesPublicController": {
"controllerName": "PagesPublic", "controllerName": "PagesPublic",
"controllerGroupName": "PagesPublic", "controllerGroupName": "PagesPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Pages.PagesPublicController", "type": "Volo.CmsKit.Public.Pages.PagesPublicController",
"interfaces": [ "interfaces": [
{ {
@ -551,6 +559,8 @@
"Volo.CmsKit.Public.Menus.MenuItemPublicController": { "Volo.CmsKit.Public.Menus.MenuItemPublicController": {
"controllerName": "MenuItemPublic", "controllerName": "MenuItemPublic",
"controllerGroupName": "MenuItemPublic", "controllerGroupName": "MenuItemPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Menus.MenuItemPublicController", "type": "Volo.CmsKit.Public.Menus.MenuItemPublicController",
"interfaces": [ "interfaces": [
{ {
@ -575,9 +585,55 @@
} }
} }
}, },
"Volo.CmsKit.Public.GlobalResources.GlobalResourcePublicController": {
"controllerName": "GlobalResourcePublic",
"controllerGroupName": "GlobalResourcePublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.GlobalResources.GlobalResourcePublicController",
"interfaces": [
{
"type": "Volo.CmsKit.Public.GlobalResources.IGlobalResourcePublicAppService"
}
],
"actions": {
"GetGlobalScriptAsync": {
"uniqueName": "GetGlobalScriptAsync",
"name": "GetGlobalScriptAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/global-resources/script",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "Volo.CmsKit.Public.GlobalResources.GlobalResourceDto",
"typeSimple": "Volo.CmsKit.Public.GlobalResources.GlobalResourceDto"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.GlobalResources.IGlobalResourcePublicAppService"
},
"GetGlobalStyleAsync": {
"uniqueName": "GetGlobalStyleAsync",
"name": "GetGlobalStyleAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/global-resources/style",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "Volo.CmsKit.Public.GlobalResources.GlobalResourceDto",
"typeSimple": "Volo.CmsKit.Public.GlobalResources.GlobalResourceDto"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.GlobalResources.IGlobalResourcePublicAppService"
}
}
},
"Volo.CmsKit.Public.Comments.CommentPublicController": { "Volo.CmsKit.Public.Comments.CommentPublicController": {
"controllerName": "CommentPublic", "controllerName": "CommentPublic",
"controllerGroupName": "CommentPublic", "controllerGroupName": "CommentPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Comments.CommentPublicController", "type": "Volo.CmsKit.Public.Comments.CommentPublicController",
"interfaces": [ "interfaces": [
{ {
@ -818,6 +874,8 @@
"Volo.CmsKit.Public.Blogs.BlogPostPublicController": { "Volo.CmsKit.Public.Blogs.BlogPostPublicController": {
"controllerName": "BlogPostPublic", "controllerName": "BlogPostPublic",
"controllerGroupName": "BlogPostPublic", "controllerGroupName": "BlogPostPublic",
"isRemoteService": true,
"apiVersion": null,
"type": "Volo.CmsKit.Public.Blogs.BlogPostPublicController", "type": "Volo.CmsKit.Public.Blogs.BlogPostPublicController",
"interfaces": [ "interfaces": [
{ {
@ -899,9 +957,9 @@
}, },
{ {
"name": "input", "name": "input",
"typeAsString": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto, Volo.Abp.Ddd.Application.Contracts", "typeAsString": "Volo.CmsKit.Public.Blogs.BlogPostGetListInput, Volo.CmsKit.Public.Application.Contracts",
"type": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", "type": "Volo.CmsKit.Public.Blogs.BlogPostGetListInput",
"typeSimple": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", "typeSimple": "Volo.CmsKit.Public.Blogs.BlogPostGetListInput",
"isOptional": false, "isOptional": false,
"defaultValue": null "defaultValue": null
} }
@ -919,6 +977,30 @@
"bindingSourceId": "Path", "bindingSourceId": "Path",
"descriptorName": "" "descriptorName": ""
}, },
{
"nameOnMethod": "input",
"name": "AuthorId",
"jsonName": null,
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Sorting",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{ {
"nameOnMethod": "input", "nameOnMethod": "input",
"name": "SkipCount", "name": "SkipCount",
@ -942,6 +1024,43 @@
"constraintTypes": null, "constraintTypes": null,
"bindingSourceId": "ModelBinding", "bindingSourceId": "ModelBinding",
"descriptorName": "input" "descriptorName": "input"
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Public.Blogs.BlogPostPublicDto>",
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Public.Blogs.BlogPostPublicDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService"
},
"GetAuthorsHasBlogPostsAsyncByInput": {
"uniqueName": "GetAuthorsHasBlogPostsAsyncByInput",
"name": "GetAuthorsHasBlogPostsAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/blog-posts/authors",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.CmsKit.Public.Blogs.BlogPostFilteredPagedAndSortedResultRequestDto, Volo.CmsKit.Public.Application.Contracts",
"type": "Volo.CmsKit.Public.Blogs.BlogPostFilteredPagedAndSortedResultRequestDto",
"typeSimple": "Volo.CmsKit.Public.Blogs.BlogPostFilteredPagedAndSortedResultRequestDto",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "input",
"name": "Filter",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}, },
{ {
"nameOnMethod": "input", "nameOnMethod": "input",
@ -954,11 +1073,72 @@
"constraintTypes": null, "constraintTypes": null,
"bindingSourceId": "ModelBinding", "bindingSourceId": "ModelBinding",
"descriptorName": "input" "descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "SkipCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "MaxResultCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
} }
], ],
"returnValue": { "returnValue": {
"type": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Public.Blogs.BlogPostPublicDto>", "type": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Users.CmsUserDto>",
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Public.Blogs.BlogPostPublicDto>" "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.CmsKit.Users.CmsUserDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService"
},
"GetAuthorHasBlogPostAsyncById": {
"uniqueName": "GetAuthorHasBlogPostAsyncById",
"name": "GetAuthorHasBlogPostAsync",
"httpMethod": "GET",
"url": "api/cms-kit-public/blog-posts/authors/{id}",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "id",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "id",
"name": "id",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.CmsKit.Users.CmsUserDto",
"typeSimple": "Volo.CmsKit.Users.CmsUserDto"
}, },
"allowAnonymous": null, "allowAnonymous": null,
"implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService" "implementFrom": "Volo.CmsKit.Public.Blogs.IBlogPostPublicAppService"

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

@ -40,8 +40,15 @@ public class BlogPostPublicController : CmsKitPublicControllerBase, IBlogPostPub
[HttpGet] [HttpGet]
[Route("authors")] [Route("authors")]
public virtual Task<List<CmsUserDto>> GetAuthorsHasBlogPostsAsync() public Task<PagedResultDto<CmsUserDto>> GetAuthorsHasBlogPostsAsync(BlogPostFilteredPagedAndSortedResultRequestDto input)
{ {
return BlogPostPublicAppService.GetAuthorsHasBlogPostsAsync(); return BlogPostPublicAppService.GetAuthorsHasBlogPostsAsync(input);
}
[HttpGet]
[Route("authors/{id}")]
public Task<CmsUserDto> GetAuthorHasBlogPostAsync(Guid id)
{
return BlogPostPublicAppService.GetAuthorHasBlogPostAsync(id);
} }
} }

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

@ -16,94 +16,133 @@
@inject IMarkdownToHtmlRenderer MarkdownRenderer @inject IMarkdownToHtmlRenderer MarkdownRenderer
@{
string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.BlogPost.Title;
var isScrollIndexEnabled = GlobalFeatureManager.Instance.IsEnabled<BlogPostScrollIndexFeature>();
}
@section styles{ @section styles{
<abp-abp-style-bundle> @if (isScrollIndexEnabled)
<abp-style src="/Pages/Public/CmsKit/Blogs/blogPost.css" /> {
<abp-style type="typeof(HighlightJsStyleContributor)" /> <abp-abp-style-bundle>
</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-abp-style-bundle>
} }
@section scripts{ @section scripts{
<abp-script-bundle> @if (isScrollIndexEnabled)
<abp-script type="typeof(HighlightJsScriptContributor)" /> {
<abp-script src="/Pages/Public/CmsKit/highlightOnLoad.js" /> <abp-script-bundle>
</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-script-bundle>
}
@{ <abp-script-bundle>
string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.BlogPost.Title; <abp-script type="typeof(HighlightJsScriptContributor)"/>
<abp-script src="/Pages/Public/CmsKit/highlightOnLoad.js"/>
</abp-script-bundle>
} }
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" /> <div class="row">
<abp-card-body> <div @Html.Raw(isScrollIndexEnabled ? "class=\"col-md-10 col-sm-12\"" : "class=\"col-md-12\"")>
<abp-row> <abp-card class="mb-4">
<div class="col-lg-8 col-md-10 mx-auto pb-4"> <img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'"/>
<h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1> <abp-card-body>
<p class="mb-lg-5 mb-md-3"> <abp-row>
<a href="/blogs/@Model.BlogSlug?authorId=@Model.BlogPost.Author.Id"> <div class="col-lg-8 col-md-10 mx-auto pb-4">
<span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span> <h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1>
</a> <p class="mb-lg-5 mb-md-3">
<small style="opacity:.65;">@Model.BlogPost.CreationTime</small> <a href="/blogs/@Model.BlogSlug?authorId=@Model.BlogPost.Author.Id">
</p> <span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span>
</a>
@if(!Model.BlogPost.Content.IsNullOrEmpty()) <small style="opacity:.65;">@Model.BlogPost.CreationTime</small>
{ </p>
@Html.Raw(await MarkdownRenderer.RenderAsync(Model.BlogPost.Content)) @if (!Model.BlogPost.Content.IsNullOrEmpty())
} {
@Html.Raw(await MarkdownRenderer.RenderAsync(Model.BlogPost.Content))
<p class="mb-3"> }
@if (Model.BlogPost.LastModificationTime != null)
{ <p class="mb-3">
<small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small> @if (Model.BlogPost.LastModificationTime != null)
} {
</p> <small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small>
<hr /> }
</p>
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>()) <hr/>
{
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, @await Component.InvokeAsync(typeof(TagViewComponent), new
entityId = Model.BlogPost.Id.ToString() {
}) entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
} entityId = Model.BlogPost.Id.ToString()
} })
</div> }
</abp-row> }
</div>
<abp-row class="row"> </abp-row>
<abp-column size-lg="_6" size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>()) <abp-row class="row">
{ <abp-column size-lg="_6" size-md="_12">
if (Model.ReactionsFeature?.IsEnabled == true) @if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
{ {
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, if (Model.ReactionsFeature?.IsEnabled == true)
entityId = Model.BlogPost.Id.ToString() {
}) @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
} {
} entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
</abp-column> entityId = Model.BlogPost.Id.ToString()
<abp-column size-lg="_6" size-md="_12"> })
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>()) }
{ }
if (Model.RatingsFeature?.IsEnabled == true) </abp-column>
{ <abp-column size-lg="_6" size-md="_12">
@await Component.InvokeAsync(typeof(RatingViewComponent), new @if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{ {
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, if (Model.RatingsFeature?.IsEnabled == true)
entityId = Model.BlogPost.Id.ToString() {
}) @await Component.InvokeAsync(typeof(RatingViewComponent), new
} {
} entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
</abp-column> entityId = Model.BlogPost.Id.ToString()
</abp-row> })
</abp-card-body> }
</abp-card> }
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
</div>
@if (isScrollIndexEnabled)
{
<div class="col-md-2 d-sm-none d-md-block">
<div id="scroll-index" class="docs-inner-anchors mt-2">
<h5>@L["InThisDocument"]</h5>
<nav id="blog-post-sticky-index" class="navbar index-scroll pt-0">
</nav>
<div class="row">
<div class="col p-0 py-3">
<a href="#" class="scroll-top-btn">
<i class="fa fa-chevron-up"></i> @L["GoToTop"]
</a>
</div>
</div>
</div>
</div>
}
</div>
@if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>()) @if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{ {

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

@ -24,19 +24,21 @@
<abp-row id="blogs-filter-area"> <abp-row id="blogs-filter-area">
<abp-column size="_4"> <abp-column size="_4">
<div class="mb-3"> <div class="mb-3">
<label class="form-label" asp-for="@Model.Authors"></label> <label class="form-label" asp-for="@Model.SelectedAuthor"></label>
<select id="AuthorSelect" class="form-control">
<option value="">@L["SelectAnAuthor"]</option> <select id="AuthorSelect" asp-for="@Model.AuthorId"
@foreach (var author in Model.Authors) class="auto-complete-select"
{ data-placeholder="@L["SelectAnAuthor"]"
if (author.Id == Model.AuthorId) data-allow-clear="true"
{ data-autocomplete-api-url="/api/cms-kit-public/blog-posts/authors"
<option value="@author.Id" selected>@author.Name @author.Surname</option> data-autocomplete-display-property="userName"
} data-autocomplete-value-property="id"
else data-autocomplete-items-property="items"
{ data-autocomplete-filter-param-name="filter">
<option value="@author.Id">@author.Name @author.Surname</option>
} @if(Model.SelectedAuthor != null)
{
<option selected value="@Model.AuthorId" selected="selected">@Model.SelectedAuthor.UserName</option>
} }
</select> </select>
</div> </div>
@ -76,4 +78,4 @@
<abp-column> <abp-column>
<abp-paginator model="Model.PagerModel"/> <abp-paginator model="Model.PagerModel"/>
</abp-column> </abp-column>
</abp-row> </abp-row>

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

@ -20,16 +20,15 @@ public class IndexModel : CmsKitPublicPageModelBase
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public int CurrentPage { get; set; } = 1; public int CurrentPage { get; set; } = 1;
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public Guid? AuthorId { get; set; } public Guid? AuthorId { get; set; }
public PagedResultDto<BlogPostPublicDto> Blogs { get; private set; } public PagedResultDto<BlogPostPublicDto> Blogs { get; private set; }
public PagerModel PagerModel => new PagerModel(Blogs.TotalCount, Blogs.Items.Count, CurrentPage, PageSize, Request.Path.ToString()); public PagerModel PagerModel => new PagerModel(Blogs.TotalCount, Blogs.Items.Count, CurrentPage, PageSize, Request.Path.ToString());
[BindProperty(SupportsGet = true)] public CmsUserDto SelectedAuthor { get; set; }
public List<CmsUserDto> Authors { get; set; }
protected IBlogPostPublicAppService BlogPostPublicAppService { get; } protected IBlogPostPublicAppService BlogPostPublicAppService { get; }
@ -48,7 +47,10 @@ public class IndexModel : CmsKitPublicPageModelBase
MaxResultCount = PageSize, MaxResultCount = PageSize,
AuthorId = AuthorId AuthorId = AuthorId
}); });
Authors = await BlogPostPublicAppService.GetAuthorsHasBlogPostsAsync(); if (AuthorId != null)
{
SelectedAuthor = await BlogPostPublicAppService.GetAuthorHasBlogPostAsync(AuthorId.Value);
}
} }
} }

17
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css

@ -20,4 +20,21 @@ span.area-title {
} }
.popover { .popover {
min-width: 276px; min-width: 276px;
}
#scroll-index {
position: sticky;
top: 20px;
}
#scroll-index .scroll-top-btn.showup{
display: block;
}
#scroll-index .scroll-top-btn{
display: none;
font-size: .85em;
color: #aaa;
text-decoration: none;
padding-left: 18px;
} }

46
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogpost-scroll-index.js

@ -0,0 +1,46 @@
$(function () {
var $myNav = $('#blog-post-sticky-index');
var $scrollToTopBtn = $('.scroll-top-btn');
window.Toc.helpers.createNavList = function () {
return $('<ul class="nav nav-pills flex-column"></ul>');
};
window.Toc.helpers.createChildNavList = function ($parent) {
var $childList = this.createNavList();
$parent.append($childList);
return $childList;
};
window.Toc.helpers.generateNavEl = function (anchor, text) {
var $a = $('<a class="nav-link"></a>');
$a.attr('href', '#' + anchor);
$a.text(text);
var $li = $('<li class="nav-item"></li>');
$li.append($a);
return $li;
};
Toc.init($myNav);
$('body').scrollspy({
target: $myNav,
});
$scrollToTopBtn.click(function () {
$('html, body').animate({scrollTop: 0}, 'fast');
});
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function () {
scrollFunction()
};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
$scrollToTopBtn.addClass('showup');
} else {
$scrollToTopBtn.removeClass('showup');
}
}
});

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

@ -0,0 +1,60 @@
/*!
* Bootstrap Table of Contents v<%= version %> (http://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
/* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */
/* All levels of nav */
nav[data-toggle='toc'] .nav > li > a {
display: block;
padding: 4px 20px;
font-size: 13px;
font-weight: 500;
color: #767676;
}
nav[data-toggle='toc'] .nav > li > a:hover,
nav[data-toggle='toc'] .nav > li > a:focus {
padding-left: 19px;
color: #563d7c;
text-decoration: none;
background-color: transparent;
border-left: 1px solid #563d7c;
}
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;
font-weight: bold;
color: #563d7c;
background-color: transparent;
border-left: 2px solid #563d7c;
}
/* Nav: second level (shown on .active) */
nav[data-toggle='toc'] .nav-link + ul {
display: none; /* Hide by default, but at >768px, show it */
padding-bottom: 10px;
}
nav[data-toggle='toc'] .nav .nav > li > a {
padding-top: 1px;
padding-bottom: 1px;
padding-left: 30px;
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;
font-weight: 500;
}
nav[data-toggle='toc'] .nav-link.active + ul {
display: block;
}

168
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/bootstrap-toc.js

@ -0,0 +1,168 @@
/*!
* Bootstrap Table of Contents v1.0.0 (http://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
(function ($) {
'use strict';
window.Toc = {
helpers: {
// return all matching elements in the set, or their descendants
findOrFilter: function ($el, selector) {
// http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
// http://stackoverflow.com/a/12731439/358804
var $descendants = $el.find(selector);
return $el
.filter(selector)
.add($descendants)
.filter(':not([data-toc-skip])');
},
generateUniqueIdBase: function (el) {
var text = $(el).text();
var anchor = text
.trim()
.toLowerCase()
.replace(/[^A-Za-z0-9]+/g, '-');
return anchor || el.tagName.toLowerCase();
},
generateUniqueId: function (el) {
var anchorBase = this.generateUniqueIdBase(el);
for (var i = 0; ; i++) {
var anchor = anchorBase;
if (i > 0) {
// add suffix
anchor += '-' + i;
}
// check if ID already exists
if (!document.getElementById(anchor)) {
return anchor;
}
}
},
generateAnchor: function (el) {
if (el.id) {
return el.id;
} else {
var anchor = this.generateUniqueId(el);
el.id = anchor;
return anchor;
}
},
createNavList: function () {
return $('<ul class="nav navbar-nav"></ul>');
},
createChildNavList: function ($parent) {
var $childList = this.createNavList();
$parent.append($childList);
return $childList;
},
generateNavEl: function (anchor, text) {
var $a = $('<a class="nav-link"></a>');
$a.attr('href', '#' + anchor);
$a.text(text);
var $li = $('<li></li>');
$li.append($a);
return $li;
},
generateNavItem: function (headingEl) {
var anchor = this.generateAnchor(headingEl);
var $heading = $(headingEl);
var text = $heading.data('toc-text') || $heading.text();
return this.generateNavEl(anchor, text);
},
// Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
getTopLevel: function ($scope) {
for (var i = 1; i <= 6; i++) {
var $headings = this.findOrFilter($scope, 'h' + i);
if ($headings.length > 1) {
return i;
}
}
return 1;
},
// returns the elements for the top level, and the next below it
getHeadings: function ($scope, topLevel) {
var topSelector = 'h' + topLevel;
var secondaryLevel = topLevel + 1;
var secondarySelector = 'h' + secondaryLevel;
return this.findOrFilter(
$scope,
topSelector + ',' + secondarySelector
);
},
getNavLevel: function (el) {
return parseInt(el.tagName.charAt(1), 10);
},
populateNav: function ($topContext, topLevel, $headings) {
var $context = $topContext;
var $prevNav;
var helpers = this;
$headings.each(function (i, el) {
var $newNav = helpers.generateNavItem(el);
var navLevel = helpers.getNavLevel(el);
// determine the proper $context
if (navLevel === topLevel) {
// use top level
$context = $topContext;
} else if ($prevNav && $context === $topContext) {
// create a new level of the tree and switch to it
$context = helpers.createChildNavList($prevNav);
} // else use the current $context
$context.append($newNav);
$prevNav = $newNav;
});
},
parseOps: function (arg) {
var opts;
if (arg.jquery) {
opts = {
$nav: arg,
};
} else {
opts = arg;
}
opts.$scope = opts.$scope || $(document.body);
return opts;
},
},
// accepts a jQuery object, or an options object
init: function (opts) {
opts = this.helpers.parseOps(opts);
// ensure that the data attribute is in place for styling
opts.$nav.attr('data-toggle', 'toc');
var $topContext = this.helpers.createChildNavList(opts.$nav);
var topLevel = this.helpers.getTopLevel(opts.$scope);
var $headings = this.helpers.getHeadings(opts.$scope, topLevel);
this.helpers.populateNav($topContext, topLevel, $headings);
},
};
$(function () {
$('nav[data-toggle="toc"]').each(function (i, el) {
var $nav = $(el);
Toc.init($nav);
});
});
})(jQuery);

22
modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js

@ -112,6 +112,28 @@
})(); })();
// controller volo.cmsKit.public.globalResources.globalResourcePublic
(function(){
abp.utils.createNamespace(window, 'volo.cmsKit.public.globalResources.globalResourcePublic');
volo.cmsKit.public.globalResources.globalResourcePublic.getGlobalScript = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-public/global-resources/script',
type: 'GET'
}, ajaxParams));
};
volo.cmsKit.public.globalResources.globalResourcePublic.getGlobalStyle = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/cms-kit-public/global-resources/style',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.cmsKit.public.comments.commentPublic // controller volo.cmsKit.public.comments.commentPublic
(function(){ (function(){

2
modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs

@ -155,7 +155,7 @@ public abstract class BlogPostRepository_Test<TStartupModule> : CmsKitTestBase<T
[Fact] [Fact]
public async Task GetAuthorsHasBlogPosts_ShouldWorkProperly() public async Task GetAuthorsHasBlogPosts_ShouldWorkProperly()
{ {
var authors = await blogPostRepository.GetAuthorsHasBlogPosts(); var authors = await blogPostRepository.GetAuthorsHasBlogPostsAsync(0, 100, null, null);
authors.ShouldNotBeNull(); authors.ShouldNotBeNull();
authors.ShouldNotBeEmpty(); authors.ShouldNotBeEmpty();

2
npm/ng-packs/packages/core/src/lib/models/environment.ts

@ -10,8 +10,8 @@ export interface Environment {
oAuthConfig: AuthConfig; oAuthConfig: AuthConfig;
production: boolean; production: boolean;
remoteEnv?: RemoteEnv; remoteEnv?: RemoteEnv;
[key:string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
} }
export interface ApplicationInfo { export interface ApplicationInfo {
name: string; name: string;
baseUrl?: string; baseUrl?: string;

Loading…
Cancel
Save