Browse Source

Merge branch 'dev' into pr/11963

pull/12073/head
enisn 5 years ago
parent
commit
8ac7bc2274
  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. 5
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  23. 5
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  24. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs
  25. 29
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs
  26. 29
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs
  27. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostFilteredPagedAndSortedResultRequestDto.cs
  28. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs
  29. 19
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  30. 18
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/BlogPostPublicClientProxy.Generated.cs
  31. 27
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.Generated.cs
  32. 7
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/GlobalResourcePublicClientProxy.cs
  33. 190
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  34. 11
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs
  35. 28
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml
  36. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml.cs
  37. 22
      modules/cms-kit/src/Volo.CmsKit.Public.Web/wwwroot/client-proxies/cms-kit-proxy.js
  38. 2
      modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs
  39. 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();
}
}
);
});
}); });

5
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",
@ -173,6 +174,8 @@
"Draft": "Draft", "Draft": "Draft",
"Publish": "Publish", "Publish": "Publish",
"BlogPostDraftConfirmationMessage": "Are you sure to set the blog post \"{0}\" as draft?", "BlogPostDraftConfirmationMessage": "Are you sure to set the blog post \"{0}\" as draft?",
"SelectAnAuthor": "Select an author" "SelectAnAuthor": "Select an author",
"RemoveCoverImageConfirmationMessage": "Are you sure to remove the cover image?",
"RemoveCoverImage": "Remove the cover image"
} }
} }

5
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",
@ -172,6 +173,8 @@
"Draft": "Taslak olarak kaydet", "Draft": "Taslak olarak kaydet",
"Publish": "Yayınla", "Publish": "Yayınla",
"BlogPostDraftConfirmationMessage": "\"{0}\" başlıklı gönderiyi taslak haline getirmek istediğinize emin misiniz?", "BlogPostDraftConfirmationMessage": "\"{0}\" başlıklı gönderiyi taslak haline getirmek istediğinize emin misiniz?",
"SelectAnAuthor": "Bir yazar seçin" "SelectAnAuthor": "Bir yazar seçin",
"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,5 +30,14 @@ 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);
} }

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,11 +105,31 @@ 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();
}
} }

29
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,

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);
} }
} }

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

@ -24,19 +24,19 @@
<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-autocomplete-api-url="/api/cms-kit-public/blog-posts/authors"
if (author.Id == Model.AuthorId) data-autocomplete-display-property="userName"
{ data-autocomplete-value-property="id"
<option value="@author.Id" selected>@author.Name @author.Surname</option> data-autocomplete-items-property="items"
} data-autocomplete-filter-param-name="filter">
else
{ @if(Model.SelectedAuthor != null)
<option value="@author.Id">@author.Name @author.Surname</option> {
} <option selected value="@Model.AuthorId" selected="selected">@Model.SelectedAuthor.UserName</option>
} }
</select> </select>
</div> </div>
@ -76,4 +76,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);
}
} }
} }

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