Browse Source

Merge branch 'dev' into berkan/cli-leptonx

pull/13306/head
Yunus Emre Kalkan 4 years ago
parent
commit
cc6ab1314b
  1. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  2. 2
      docs/en/CLI.md
  3. 10
      docs/zh-Hans/CLI.md
  4. 46
      docs/zh-Hans/Redis-Cache.md
  5. 8
      docs/zh-Hans/docs-nav.json
  6. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationOptions.cs
  7. 17
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationContributorContext.cs
  8. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  9. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IApplicationConfigurationContributor.cs
  10. 21
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  11. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  12. 29
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs
  13. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs
  14. 14
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeApplicationIdGuidStep.cs
  15. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Events/ProjectCreationProgressEvent.cs
  16. 17
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs
  17. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
  18. 34
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangeApplicationIdGuidStep.cs
  19. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiTemplateBase.cs
  20. 19
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpDictionaryExtensions.cs
  21. 4
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  22. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  23. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs
  24. 13
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TestApplicationConfigurationContributor.cs
  25. 2
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/appsettings.json
  26. 8
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentConsts.cs
  27. 10
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentFragment.cs
  28. 18
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/CmsKitContentWidgetOptions.cs
  29. 120
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentParser.cs
  30. 11
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentWidgetConfig.cs
  31. 4
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs
  32. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs
  33. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/PageDto.cs
  34. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs
  35. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs
  36. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs
  37. 63
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json
  38. 17
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml
  39. 54
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/DefaultPageViewComponent.cs
  40. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs
  41. 115
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  42. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml.cs
  43. 41
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
  44. 8
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml.cs
  45. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/PageViewModel.cs
  46. 138
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentParser_Test.cs
  47. 13
      modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Pages/PageManager_Test.cs
  48. 4
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs
  49. 13
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220623064459_Initial.Designer.cs
  50. 2
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220623064459_Initial.cs
  51. 11
      modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs
  52. 19
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/LogoutController.cs
  53. 22
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Views/Logout/Logout.cshtml
  54. 6
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/el.json
  55. 4
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/en.json
  56. 6
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/pt-BR.json
  57. 4
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/tr.json
  58. 4
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/zh-Hans.json
  59. 4
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/zh-Hant.json
  60. 135
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml
  61. 28
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs
  62. 18
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs

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

@ -400,6 +400,7 @@
"UserDeveloperDescription": "The 'Developers' can write code in the ABP Commercial projects, download the ABP sample projects, and ask questions on the support website. On the other hand, the 'Developers' cannot manage this organization.",
"RemoveCurrentUserFromOrganizationWarningMessage": "You are removing yourself from your own organization. You will no longer be able to manage this organization, do you confirm?",
"RenewExistingOrganizationOrCreateNewOneMessage": "You can renew the license of your organization(s) by clicking the below \"Extend Now\" button(s) and thus you can extend the license expiration date by 1 year. If you continue to checkout, you will have a new organization. Do you want to continue with a new organization?",
"PurchaseTrialOrganizationOrCreateNewOneMessage": "You have a trial license. To purchase your trial license click Purchase Now button. If you continue to checkout, you will have a new organization. Do you want to continue with a new organization?",
"ExtendNow": "Extend Now",
"CreateNewOrganization": "Create a new organization",
"RenewLicenseEarly": "If I renew my license early, will I get the full year?",

2
docs/en/CLI.md

@ -219,7 +219,7 @@ Example:
abp add-package Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
````
* This example adds the Volo.Abp.MongoDB package to the project.
* This example adds the `Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic` package to the project.
#### Options

10
docs/zh-Hans/CLI.md

@ -190,10 +190,10 @@ abp add-package <包名> [options]
示例:
````
abp add-package Volo.Abp.MongoDB
abp add-package Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
````
* 示例中将Volo.Abp.MongoDB包添加到项目中.
* 示例中将`Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic`包添加到项目中.
#### Options
@ -201,6 +201,12 @@ abp add-package Volo.Abp.MongoDB
* `--with-source-code`: 下载包的源码到你的解决方案文件夹,而不是NuGet/NPM软件包.
* `--add-to-solution-file`: 添加下载/创建的包添加到解决方案文件中,你在IDE中打开解决方案时也会看到包的项目. (仅当 `--with-source-code` 为 `True` 时可用.)
> 当前只有基本主题包([MVC](https://docs.abp.io/zh-Hans/abp/latest/UI/AspNetCore/Basic-Theme) 和 [Blazor](https://docs.abp.io/zh-Hans/abp/latest/UI/Blazor/Basic-Theme)) 可以下载.
> - Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
> - Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme
> - Volo.Abp.AspNetCore.Components.Web.BasicTheme
> - Volo.Abp.AspNetCore.Components.Server.BasicTheme
### add-module
通过查找模块的所有包,查找解决方案中的相关项目,并将每个包添加到解决方案中的相应项目,从而将[多包应用程序模块](Modules/Index)添加到解决方案中.

46
docs/zh-Hans/Redis-Cache.md

@ -1 +1,45 @@
TODO...
# Redis 缓存
ABP Framework [缓存系统](Caching.md) 拓展了 [ASP.NET Core 分布式缓存](https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed). 因此, 标准 ASP.NET Core 分布式缓存支持的 **任何提供程序** 都可以在你的应用程序中使用,并且可以像 **微软文档** 那样进行配置.
不过, ABP为 Redis Cache 还提供了一个集成包: [Volo.Abp.Caching.StackExchangeRedis](https://www.nuget.org/packages/Volo.Abp.Caching.StackExchangeRedis).
为何使用中这个包而不是标准的[Microsoft.Extensions.Caching.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.StackExchangeRedis/)包有两个原因.
1. 它实现 `SetManyAsync` 和 `GetManyAsync` 方法. 这些都不是 Microsoft 缓存库的标准方法,而是由ABP框架[缓存](Caching.md)系统添加的. 当你需要通过单个方法调用设置/获取多个缓存项时,它们**显著提高了性能**.
2. 它 **简化** 了 Redis 缓存 **配置** (将在下面解释).
> Volo.Abp.Caching.StackExchangeRedis 已经使用了 Microsoft.Extensions.Caching.StackExchangeRedis 包,但对其进行了扩展和改进.
## 安装
> 如果使用Redis,则此软件包已安装在应用程序启动模板中.
在项目`.csproj`的文件夹中打开命令行窗口并输入命令:
````bash
abp add-package Volo.Abp.Caching.StackExchangeRedis
````
## 配置
Volo.Abp.Caching.StackExchangeRedis 包自动从`IConfiguration`获取Redis[配置](Configuration.md). 因此,你可以在以下位置设置配置 `appsettings.json`:
````js
"Redis": {
"IsEnabled": "true",
"Configuration": "127.0.0.1"
}
````
设置`IsEnabled`为可选的,如果未设置将默认视为`true`.
或者,你可以在[模块](Module-Development-Basics.md)的ConfigureServices方法中配置标准的[RedisCacheOptions](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.caching.stackexchangeredis.rediscacheoptions)类:
````csharp
Configure<RedisCacheOptions>(options =>
{
//...
});
````
## 另请参阅
* [缓存](Caching.md)

8
docs/zh-Hans/docs-nav.json

@ -167,7 +167,13 @@
},
{
"text": "缓存",
"path": "Caching.md"
"path": "Caching.md",
"items": [
{
"text": "Redis 缓存",
"path": "Redis-Cache.md"
}
]
},
{
"text": "日志",

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationOptions.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
public class AbpApplicationConfigurationOptions
{
public List<IApplicationConfigurationContributor> Contributors { get; }
public AbpApplicationConfigurationOptions()
{
Contributors = new List<IApplicationConfigurationContributor>();
}
}

17
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationContributorContext.cs

@ -0,0 +1,17 @@
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
public class ApplicationConfigurationContributorContext : IServiceProviderAccessor
{
public IServiceProvider ServiceProvider { get; }
public ApplicationConfigurationDto ApplicationConfiguration { get; }
public ApplicationConfigurationContributorContext(IServiceProvider serviceProvider, ApplicationConfigurationDto applicationConfiguration)
{
ServiceProvider = serviceProvider;
ApplicationConfiguration = applicationConfiguration;
}
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs

@ -1,11 +1,12 @@
using System;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Data;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
[Serializable]
public class ApplicationConfigurationDto
public class ApplicationConfigurationDto : IHasExtraProperties
{
public ApplicationLocalizationConfigurationDto Localization { get; set; }
@ -28,4 +29,6 @@ public class ApplicationConfigurationDto
public ClockDto Clock { get; set; }
public ObjectExtensionsDto ObjectExtensions { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; set; }
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IApplicationConfigurationContributor.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
public interface IApplicationConfigurationContributor
{
Task ContributeAsync(ApplicationConfigurationContributorContext context);
}

21
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -13,6 +13,7 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Authorization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.Features;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Localization;
@ -41,6 +42,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
private readonly ITimezoneProvider _timezoneProvider;
private readonly AbpClockOptions _abpClockOptions;
private readonly ICachedObjectExtensionsDtoService _cachedObjectExtensionsDtoService;
private readonly AbpApplicationConfigurationOptions _options;
public AbpApplicationConfigurationAppService(
IOptions<AbpLocalizationOptions> localizationOptions,
@ -58,7 +60,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
ILanguageProvider languageProvider,
ITimezoneProvider timezoneProvider,
IOptions<AbpClockOptions> abpClockOptions,
ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService)
ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService,
IOptions<AbpApplicationConfigurationOptions> options)
{
_serviceProvider = serviceProvider;
_abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider;
@ -74,6 +77,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
_timezoneProvider = timezoneProvider;
_abpClockOptions = abpClockOptions.Value;
_cachedObjectExtensionsDtoService = cachedObjectExtensionsDtoService;
_options = options.Value;
_localizationOptions = localizationOptions.Value;
_multiTenancyOptions = multiTenancyOptions.Value;
}
@ -96,9 +100,22 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
CurrentTenant = GetCurrentTenant(),
Timing = await GetTimingConfigAsync(),
Clock = GetClockConfig(),
ObjectExtensions = _cachedObjectExtensionsDtoService.Get()
ObjectExtensions = _cachedObjectExtensionsDtoService.Get(),
ExtraProperties = new ExtraPropertyDictionary()
};
if (_options.Contributors.Any())
{
using (var scope = _serviceProvider.CreateScope())
{
var context = new ApplicationConfigurationContributorContext(scope.ServiceProvider, result);
foreach (var contributor in _options.Contributors)
{
await contributor.ContributeAsync(context);
}
}
}
Logger.LogDebug("Executed AbpApplicationConfigurationAppService.GetAsync().");
return result;

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -16,13 +16,14 @@ using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectModification;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Local;
namespace Volo.Abp.Cli.Commands;
public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransientDependency
{
public const string Name = "new";
protected TemplateProjectBuilder TemplateProjectBuilder { get; }
public ITemplateInfoProvider TemplateInfoProvider { get; }
@ -34,8 +35,9 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
IInstallLibsService installLibsService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder)
: base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder, initialMigrationCreator, themePackageAdder)
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
: base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder, initialMigrationCreator, themePackageAdder, eventBus)
{
TemplateProjectBuilder = templateProjectBuilder;
TemplateInfoProvider = templateInfoProvider;
@ -81,10 +83,10 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
Logger.LogInformation($"'{projectName}' has been successfully created to '{projectArgs.OutputFolder}'");
ConfigureNpmPackagesForTheme(projectArgs);
RunGraphBuildForMicroserviceServiceTemplate(projectArgs);
await RunGraphBuildForMicroserviceServiceTemplate(projectArgs);
await CreateInitialMigrationsAsync(projectArgs);
await RunInstallLibsForWebTemplateAsync(projectArgs);
ConfigurePwaSupportForAngular(projectArgs);
await ConfigurePwaSupportForAngular(projectArgs);
OpenRelatedWebPage(projectArgs, template, isTiered, commandLineArgs);
}

29
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs

@ -14,10 +14,12 @@ using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Events;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Microservice;
using Volo.Abp.Cli.ProjectBuilding.Templates.Module;
using Volo.Abp.Cli.Utils;
using Volo.Abp.EventBus.Local;
namespace Volo.Abp.Cli.Commands;
@ -29,6 +31,7 @@ public abstract class ProjectCreationCommandBase
public IInstallLibsService InstallLibsService { get; }
public AngularPwaSupportAdder AngularPwaSupportAdder { get; }
public InitialMigrationCreator InitialMigrationCreator { get; }
public ILocalEventBus EventBus { get; }
public ILogger<NewCommand> Logger { get; set; }
public ThemePackageAdder ThemePackageAdder { get; }
@ -40,7 +43,8 @@ public abstract class ProjectCreationCommandBase
IInstallLibsService installLibsService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder)
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
{
ConnectionStringProvider = connectionStringProvider;
SolutionPackageVersionFinder = solutionPackageVersionFinder;
@ -48,6 +52,7 @@ public abstract class ProjectCreationCommandBase
InstallLibsService = installLibsService;
AngularPwaSupportAdder = angularPwaSupportAdder;
InitialMigrationCreator = initialMigrationCreator;
EventBus = eventBus;
ThemePackageAdder = themePackageAdder;
Logger = NullLogger<NewCommand>.Instance;
@ -201,6 +206,10 @@ public abstract class ProjectCreationCommandBase
protected void ExtractProjectZip(ProjectBuildResult project, string outputFolder)
{
EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Extracting the solution archieve"
}, false);
using (var templateFileStream = new MemoryStream(project.ZipContent))
{
using (var zipInputStream = new ZipInputStream(templateFileStream))
@ -332,10 +341,14 @@ public abstract class ProjectCreationCommandBase
throw new CliUsageException(ExceptionMessageHelper.GetInvalidArgExceptionMessage("Database Provider"));
}
protected virtual void RunGraphBuildForMicroserviceServiceTemplate(ProjectBuildArgs projectArgs)
protected virtual async Task RunGraphBuildForMicroserviceServiceTemplate(ProjectBuildArgs projectArgs)
{
if (MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(projectArgs.TemplateName))
{
await EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Building the microservice solution"
}, false);
CmdHelper.RunCmd("dotnet build /graphbuild", projectArgs.OutputFolder);
}
}
@ -348,6 +361,11 @@ public abstract class ProjectCreationCommandBase
MicroserviceServiceTemplateBase.IsMicroserviceTemplate(projectArgs.TemplateName))
{
Logger.LogInformation("Installing client-side packages...");
await EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Installing client-side packages"
}, false);
await InstallLibsService.InstallLibsAsync(projectArgs.OutputFolder);
}
}
@ -384,10 +402,14 @@ public abstract class ProjectCreationCommandBase
return;
}
await EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Creating the initial DB migration"
}, false);
await InitialMigrationCreator.CreateAsync(Path.GetDirectoryName(efCoreProjectPath), isLayeredTemplate);
}
protected void ConfigurePwaSupportForAngular(ProjectBuildArgs projectArgs)
protected async Task ConfigurePwaSupportForAngular(ProjectBuildArgs projectArgs)
{
var isAngular = projectArgs.UiFramework == UiFramework.Angular;
var isPwa = projectArgs.Pwa;
@ -395,6 +417,7 @@ public abstract class ProjectCreationCommandBase
if (isAngular && isPwa)
{
Logger.LogInformation("Adding PWA Support to Angular app.");
AngularPwaSupportAdder.AddPwaSupport(projectArgs.OutputFolder);
}
}

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs

@ -116,6 +116,18 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency
return;
}
var IsSolutionBuiltResponse = await client.GetAsync(
$"{AbpSuiteHost}/api/abpSuite/solutions/{solutionId.ToString()}/is-built"
);
var IsSolutionBuilt = Convert.ToBoolean(await IsSolutionBuiltResponse.Content.ReadAsStringAsync());
if (!IsSolutionBuilt)
{
Logger.LogInformation("Building the solution...");
CmdHelper.RunCmd("dotnet build", Path.GetDirectoryName(solutionFile));
}
var entityContent = new StringContent(
File.ReadAllText(entityFile),
Encoding.UTF8,

14
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeApplicationIdGuidStep.cs

@ -1,14 +0,0 @@
using System;
using System.Linq;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps;
public class ChangeApplicationIdGuidStep: ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.csproj"));
projectFile?.SetContent(projectFile.Content.Replace("27317750-B571-4690-B433-B358B2480E01", Guid.NewGuid().ToString()));
}
}

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Events/ProjectCreationProgressEvent.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.Cli.ProjectBuilding.Events;
public class ProjectCreationProgressEvent
{
public string Message { get; set; }
}

17
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs

@ -10,8 +10,10 @@ using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Licensing;
using Volo.Abp.Cli.ProjectBuilding.Analyticses;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Events;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ProjectBuilding;
@ -26,6 +28,7 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency
protected AbpCliOptions Options { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IApiKeyService ApiKeyService { get; }
protected ILocalEventBus EventBus { get; }
private readonly IConfiguration _configuration;
@ -35,7 +38,8 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency
IOptions<AbpCliOptions> options,
IJsonSerializer jsonSerializer,
IApiKeyService apiKeyService,
IConfiguration configuration)
IConfiguration configuration,
ILocalEventBus eventBus)
{
SourceCodeStore = sourceCodeStore;
TemplateInfoProvider = templateInfoProvider;
@ -43,6 +47,7 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency
Options = options.Value;
JsonSerializer = jsonSerializer;
ApiKeyService = apiKeyService;
EventBus = eventBus;
_configuration = configuration;
Logger = NullLogger<TemplateProjectBuilder>.Instance;
@ -54,6 +59,10 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency
NormalizeArgs(args, templateInfo);
await EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Downloading the solution template"
}, false);
var templateFile = await SourceCodeStore.GetAsync(
args.TemplateName,
SourceCodeTypes.Template,
@ -116,7 +125,11 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency
{
appTemplateBase.HasDbMigrations = SemanticVersion.Parse(templateFile.Version) < new SemanticVersion(4, 3, 99);
}
await EventBus.PublishAsync(new ProjectCreationProgressEvent {
Message = "Customizing the solution template"
}, false);
TemplateProjectBuildPipelineBuilder.Build(context).Execute();
if (!templateInfo.DocumentUrl.IsNullOrEmpty())

4
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs

@ -359,6 +359,7 @@ public abstract class AppTemplateBase : TemplateInfo
else
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer"));
steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305"));
@ -398,6 +399,7 @@ public abstract class AppTemplateBase : TemplateInfo
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server.Tiered"));
steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.Blazor.Server", "MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer"));
steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44313"));
@ -419,6 +421,7 @@ public abstract class AppTemplateBase : TemplateInfo
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer"));
steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44303"));
}
@ -449,6 +452,7 @@ public abstract class AppTemplateBase : TemplateInfo
else
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer"));
steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host"));
steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305"));

34
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangeApplicationIdGuidStep.cs

@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Xml;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.Utils;
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Maui;
public class MauiChangeApplicationIdGuidStep: ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.csproj") || f.Name.EndsWith("MyCompanyName.MyProjectName.Maui.csproj"));
if (projectFile == null)
{
return;
}
using (var stream = StreamHelper.GenerateStreamFromString(projectFile.Content))
{
var doc = new XmlDocument { PreserveWhitespace = true };
doc.Load(stream);
var node = doc.SelectSingleNode("/Project/PropertyGroup/ApplicationIdGuid");
if (node != null)
{
node.InnerText = Guid.NewGuid().ToString();
}
projectFile.SetContent(doc.OuterXml);
}
}
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiTemplateBase.cs

@ -16,7 +16,7 @@ public class MauiTemplateBase: TemplateInfo
{
var steps = new List<ProjectBuildPipelineStep>
{
new ChangeApplicationIdGuidStep()
new MauiChangeApplicationIdGuidStep()
};
return steps;

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

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

4
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -223,7 +223,7 @@ public abstract class AbpApplicationBase : IAbpApplication
{
if (_configuredServices)
{
throw new AbpInitializationException("Services have already been configured! If you call ConfigureServicesAsync method, you must have set AbpApplicationCreationOptions.SkipConfigureServices tu true before.");
throw new AbpInitializationException("Services have already been configured! If you call ConfigureServicesAsync method, you must have set AbpApplicationCreationOptions.SkipConfigureServices to true before.");
}
}
@ -231,7 +231,7 @@ public abstract class AbpApplicationBase : IAbpApplication
public virtual void ConfigureServices()
{
CheckMultipleConfigureServices();
var context = new ServiceConfigurationContext(Services);
Services.AddSingleton(context);

6
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.Mvc.GlobalFeatures;
using Volo.Abp.AspNetCore.Mvc.Localization;
@ -127,6 +128,11 @@ public class AbpAspNetCoreMvcTestModule : AbpModule
options.Maps.Add("SerialNumber", () => ClaimTypes.SerialNumber);
options.Maps.Add("DateOfBirth", () => ClaimTypes.DateOfBirth);
});
Configure<AbpApplicationConfigurationOptions>(options =>
{
options.Contributors.Add(new TestApplicationConfigurationContributor());
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder_Tests.cs

@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Data;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
@ -15,5 +16,6 @@ public class ApplicationConfigurationBuilder_Tests : AspNetCoreMvcTestBase
config.Auth.ShouldNotBeNull();
config.Localization.ShouldNotBeNull();
config.GetProperty("TestKey").ShouldBe("TestValue");
}
}

13
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TestApplicationConfigurationContributor.cs

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Volo.Abp.Data;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
public class TestApplicationConfigurationContributor : IApplicationConfigurationContributor
{
public Task ContributeAsync(ApplicationConfigurationContributorContext context)
{
context.ApplicationConfiguration.SetProperty("TestKey", "TestValue");
return Task.CompletedTask;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

13
modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220425082215_Initial.Designer.cs → modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220623064459_Initial.Designer.cs

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace OpenIddict.Demo.Server.Migrations
{
[DbContext(typeof(ServerDbContext))]
[Migration("20220425082215_Initial")]
[Migration("20220623064459_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -21,7 +21,7 @@ namespace OpenIddict.Demo.Server.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "6.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
@ -336,7 +336,8 @@ namespace OpenIddict.Demo.Server.Migrations
.HasColumnName("ExtraProperties");
b.Property<bool>("IsActive")
.HasColumnType("bit");
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
@ -682,6 +683,9 @@ namespace OpenIddict.Demo.Server.Migrations
b.Property<string>("ClientSecret")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClientUri")
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
@ -732,6 +736,9 @@ namespace OpenIddict.Demo.Server.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("LogoUri")
.HasColumnType("nvarchar(max)");
b.Property<string>("Permissions")
.HasColumnType("nvarchar(max)");

2
modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220425082215_Initial.cs → modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20220623064459_Initial.cs

@ -237,6 +237,8 @@ namespace OpenIddict.Demo.Server.Migrations
RedirectUris = table.Column<string>(type: "nvarchar(max)", nullable: true),
Requirements = table.Column<string>(type: "nvarchar(max)", nullable: true),
Type = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
ClientUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
LogoUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),

11
modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs

@ -19,7 +19,7 @@ namespace OpenIddict.Demo.Server.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "6.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
@ -334,7 +334,8 @@ namespace OpenIddict.Demo.Server.Migrations
.HasColumnName("ExtraProperties");
b.Property<bool>("IsActive")
.HasColumnType("bit");
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
@ -680,6 +681,9 @@ namespace OpenIddict.Demo.Server.Migrations
b.Property<string>("ClientSecret")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClientUri")
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
@ -730,6 +734,9 @@ namespace OpenIddict.Demo.Server.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("LogoUri")
.HasColumnType("nvarchar(max)");
b.Property<string>("Permissions")
.HasColumnType("nvarchar(max)");

19
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/LogoutController.cs

@ -10,21 +10,12 @@ namespace Volo.Abp.OpenIddict.Controllers;
public class LogoutController : AbpOpenIdDictControllerBase
{
[HttpGet]
public virtual Task<IActionResult> GetAsync()
public virtual async Task<IActionResult> GetAsync()
{
return Task.FromResult<IActionResult>(View("Logout"));
}
[HttpPost]
public virtual async Task<IActionResult> PostAsync()
{
if (await HasFormValueAsync("accept"))
{
// Ask ASP.NET Core Identity to delete the local and external cookies created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await SignInManager.SignOutAsync();
}
// Ask ASP.NET Core Identity to delete the local and external cookies created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await SignInManager.SignOutAsync();
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
// to the post_logout_redirect_uri specified by the client application or to

22
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Views/Logout/Logout.cshtml

@ -1,22 +0,0 @@
@using Microsoft.Extensions.Primitives
@using Volo.Abp.OpenIddict.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@model AuthorizeViewModel
@inject IHtmlLocalizer<AbpOpenIddictResource> L
<div class="jumbotron">
<h1>@L["LogOut"]</h1>
<p class="lead text-left">@L["AreYouSureYouWantToSignOut"]</p>
<form method="post" action="~/connect/logout">
@Html.AntiForgeryToken()
@* Flow the request parameters so they can be received by the LogoutPost action: *@
@foreach (var parameter in Context.Request.HasFormContentType ? (IEnumerable<KeyValuePair<string, StringValues>>) Context.Request.Form : Context.Request.Query)
{
<input type="hidden" name="@parameter.Key" value="@parameter.Value"/>
}
<input class="btn btn-primary" name="accept" type="submit" value="@L["Accept"]"/>
<input class="btn btn-danger ms-1" name="deny" type="submit" value="@L["Deny"]"/>
</form>
</div>

6
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/el.json

@ -10,8 +10,6 @@
"DoYouWantToGrantAccessToYourData": "Θέλετε να παραχωρήσετε στον χρήστη {0} πρόσβαση στα δεδομένα σας;",
"ScopesRequested": "Ζητούνται πεδία εφαρμογής",
"Accept": "Αποδοχή",
"Deny": "Άρνηση",
"LogOut": "Αποσύνδεση",
"AreYouSureYouWantToSignOut": "Είστε βέβαιοι ότι θέλετε να αποσυνδεθείτε;"
"Deny": "Άρνηση"
}
}
}

4
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/en.json

@ -10,8 +10,6 @@
"DoYouWantToGrantAccessToYourData": "Do you want to grant {0} access to your data?",
"ScopesRequested": "Scopes requested",
"Accept": "Accept",
"Deny": "Deny",
"LogOut": "Log out",
"AreYouSureYouWantToSignOut": "Are you sure you want to sign out?"
"Deny": "Deny"
}
}

6
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/pt-BR.json

@ -10,8 +10,6 @@
"DoYouWantToGrantAccessToYourData": "Deseja permitir {0} acesse seus dados?",
"ScopesRequested": "Escopo solicitado",
"Accept": "Aceitar",
"Deny": "Negar",
"LogOut": "Sair",
"AreYouSureYouWantToSignOut": "Tem certeza que deseja sair?"
"Deny": "Negar"
}
}
}

4
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/tr.json

@ -10,8 +10,6 @@
"DoYouWantToGrantAccessToYourData": "Do you want to grant {0} access to your data?",
"ScopesRequested": "İstenen kapsamlar",
"Accept": "Kabul etmek",
"Deny": "Reddetmek",
"LogOut": "Çıkış Yap",
"AreYouSureYouWantToSignOut": "Çıkış yapmak istediğinden emin misin?"
"Deny": "Reddetmek"
}
}

4
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/zh-Hans.json

@ -10,8 +10,6 @@
"DoYouWantToGrantAccessToYourData": "是否要授予 {0} 访问你的数据的权限?",
"ScopesRequested": "要求的Scope",
"Accept": "同意",
"Deny": "拒绝",
"LogOut": "注销",
"AreYouSureYouWantToSignOut": "你确定要退出吗?"
"Deny": "拒绝"
}
}

4
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/Localization/OpenIddict/zh-Hant.json

@ -11,8 +11,6 @@
"DoYouWantToGrantAccessToYourData": "是否要授予 {0} 訪問你的數據的權限?",
"ScopesRequested": "要求的Scope",
"Accept": "接受",
"Deny": "拒絕",
"LogOut": "註銷",
"AreYouSureYouWantToSignOut": "你確定要退出嗎?"
"Deny": "拒絕"
}
}

135
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml

@ -1,34 +1,119 @@
@page
@using Microsoft.AspNetCore.Http.Extensions
@using Microsoft.AspNetCore.Mvc.Localization
@using MyCompanyName.MyProjectName.Pages
@using MyCompanyName.MyProjectName.Localization
@using Volo.Abp.Users
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.Ui.Branding
@model IndexModel
@inject IHtmlLocalizer<MyProjectNameResource> L
@inject ICurrentUser CurrentUser
@if (CurrentUser.IsAuthenticated)
{
<div>
@inject IBrandingProvider BrandingProvider
@inject ITheme Theme
@{
Layout = Theme.GetEmptyLayout();
}
<div class="d-flex align-items-center" style="min-height: 100vh;">
<div class="container">
<abp-row>
<abp-column size-md="_3" class="text-center">
<i class="fa fa-user d-block" style="font-size: 10em; color: #12b900"></i>
<a abp-button="Primary" href="/Account/Logout">Logout</a>
</abp-column>
<abp-column size-md="_9">
<h2>@CurrentUser.UserName</h2>
<h5 class="text-muted">@CurrentUser.Email</h5>
<div>
<strong>Roles</strong>: @CurrentUser.Roles.JoinAsString(", ")
<br />
<strong>Claims</strong>: <br />
@Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString(" <br /> "))
<div class="col mx-auto account-column">
<div class="account-brand p-4 text-center mb-1">
@if (!BrandingProvider.LogoUrl.IsNullOrEmpty())
{
<a class="navbar-brand" href="/" alt="@BrandingProvider.AppName"></a>
}
else
{
<h1>@BrandingProvider.AppName</h1>
}
</div>
</abp-column>
</abp-row>
</div>
}
<abp-card>
<abp-card-body>
<div class="container">
<abp-row>
<abp-column size="_9">
<div class="d-flex align-items-center">
<div class="me-3 p-2">
<i class="fa fa-user d-block" style="font-size: 10em; color: #12b900"></i>
</div>
<div class="p2">
@if (CurrentUser.IsAuthenticated)
{
<span class="fs-16">
@L["Welcome"] <span class="fw-7">@CurrentUser.UserName</span>
</span>
<span class="fs-14 d-block text-dark-800 opacity-75 mb-1">@CurrentUser.Email</span>
<div class="d-grid gap-2">
<a abp-button="Outline_Primary" asp-controller="Manage" asp-action="Index" asp-area="Account">@L["MyAccount"]</a>
<a abp-button="Primary" asp-controller="Logout" asp-action="Index" asp-area="Account">@L["Logout"]</a>
</div>
}
else
{
<a abp-button="Primary" asp-controller="Login" asp-action="Index" asp-area="Account">@L["Login"]</a>
}
</div>
@if (!CurrentUser.IsAuthenticated)
{
<div class="text-center">
<i class="fa fa-user d-block" style="font-size: 10em; color: #aaa"></i><br/><br />
<a abp-button="Primary" asp-page="/Account/Login">Login</a>
</div>
</abp-column>
<abp-column size="_3">
<div class="ml-auto p-2 float-end">
<abp-dropdown>
<abp-dropdown-button text="@Model.CurrentLanguage" />
<abp-dropdown-menu>
@foreach (var language in Model.Languages)
{
<abp-dropdown-item href="~/Abp/Languages/Switch?culture=@(language.CultureName)&uiCulture=@(language.UiCultureName)&returnUrl=@(System.Net.WebUtility.UrlEncode(Request.GetEncodedPathAndQuery()))">@language.DisplayName</abp-dropdown-item>
}
</abp-dropdown-menu>
</abp-dropdown>
</div>
</abp-column>
</abp-row>
<hr class="m-4" />
<abp-row>
@foreach (var application in Model.Applications)
{
<abp-column size-md="_4" class="mb-2">
<abp-card>
<abp-card-body>
@if (!application.LogoUri.IsNullOrEmpty())
{
<div class="mx-auto">
<img src="@application.LogoUri" style="height:64px" class="mb-3" />
</div>
}
<h4>@application.DisplayName</h4>
<span class="text-muted">@application.ClientUri</span>
<div class="mt-1">
<a abp-button="Outline_Secondary" href="@application.ClientUri">@L["Visit"]</a>
</div>
</abp-card-body>
</abp-card>
</abp-column>
}
</abp-row>
</div>
</abp-card-body>
</abp-card>
</div>
</abp-row>
</div>
}
</div>

28
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs

@ -1,10 +1,36 @@
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.Localization;
using Volo.Abp.OpenIddict.Applications;
namespace MyCompanyName.MyProjectName.Pages;
public class IndexModel : AbpPageModel
{
public void OnGet()
public List<OpenIddictApplication> Applications { get; protected set; }
public IReadOnlyList<LanguageInfo> Languages { get; protected set; }
public string CurrentLanguage { get; protected set; }
protected IOpenIddictApplicationRepository OpenIdApplicationRepository { get; }
protected ILanguageProvider LanguageProvider { get; }
public IndexModel(IOpenIddictApplicationRepository openIdApplicationmRepository, ILanguageProvider languageProvider)
{
OpenIdApplicationRepository = openIdApplicationmRepository;
LanguageProvider = languageProvider;
}
public async Task OnGetAsync()
{
Applications = await OpenIdApplicationRepository.GetListAsync();
Languages = await LanguageProvider.GetLanguagesAsync();
CurrentLanguage = CultureInfo.CurrentCulture.DisplayName;
}
}

18
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs

@ -10,6 +10,7 @@ using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.OpenIddict.Applications;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
@ -21,14 +22,14 @@ namespace MyCompanyName.MyProjectName.OpenIddict;
public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IConfiguration _configuration;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IAbpApplicationManager _applicationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IStringLocalizer<OpenIddictResponse> L;
public OpenIddictDataSeedContributor(
IConfiguration configuration,
IOpenIddictApplicationManager applicationManager,
IAbpApplicationManager applicationManager,
IOpenIddictScopeManager scopeManager,
IPermissionDataSeeder permissionDataSeeder,
IStringLocalizer<OpenIddictResponse> l)
@ -98,6 +99,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
},
scopes: commonScopes,
redirectUri: $"{webClientRootUrl}signin-oidc",
clientUri: webClientRootUrl,
postLogoutRedirectUri: $"{webClientRootUrl}signout-callback-oidc"
);
}
@ -122,6 +124,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
},
scopes: commonScopes,
redirectUri: consoleAndAngularClientRootUrl,
clientUri: consoleAndAngularClientRootUrl,
postLogoutRedirectUri: consoleAndAngularClientRootUrl
);
}
@ -144,6 +147,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
},
scopes: commonScopes,
redirectUri: $"{blazorRootUrl}/authentication/login-callback",
clientUri: blazorRootUrl,
postLogoutRedirectUri: $"{blazorRootUrl}/authentication/logout-callback"
);
}
@ -167,6 +171,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
},
scopes: commonScopes,
redirectUri: $"{blazorServerTieredRootUrl}signin-oidc",
clientUri: blazorServerTieredRootUrl,
postLogoutRedirectUri: $"{blazorServerTieredRootUrl}signout-callback-oidc"
);
}
@ -188,7 +193,8 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
OpenIddictConstants.GrantTypes.AuthorizationCode,
},
scopes: commonScopes,
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html"
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html",
clientUri: swaggerRootUrl
);
}
}
@ -201,6 +207,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
string secret,
List<string> grantTypes,
List<string> scopes,
string clientUri = null,
string redirectUri = null,
string postLogoutRedirectUri = null,
List<string> permissions = null)
@ -224,13 +231,14 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
var client = await _applicationManager.FindByClientIdAsync(name);
if (client == null)
{
var application = new OpenIddictApplicationDescriptor
var application = new AbpApplicationDescriptor
{
ClientId = name,
Type = type,
ClientSecret = secret,
ConsentType = consentType,
DisplayName = displayName
DisplayName = displayName,
ClientUri = clientUri,
};
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));

Loading…
Cancel
Save