Browse Source

Merge branch 'dev' into ui-extensions-blazor

pull/6647/head
Ilkay Ilknur 6 years ago
parent
commit
0e7c6e026f
  1. 24
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 21
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
  3. 25
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
  4. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  5. 31
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  6. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  7. 20
      docs/en/Background-Jobs-RabbitMq.md
  8. 2
      docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
  9. 22
      docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
  10. 101
      docs/en/UI/Angular/HTTP-Requests.md
  11. 2
      docs/en/UI/Angular/Router-Events.md
  12. 4
      docs/en/UI/Angular/Service-Proxies.md
  13. BIN
      docs/en/UI/Angular/images/custom-error-handler-404-component.jpg
  14. BIN
      docs/en/UI/Angular/images/custom-error-handler-toaster-message.jpg
  15. 154
      docs/zh-Hans/Background-Jobs-RabbitMq.md
  16. 20
      docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
  17. 2
      docs/zh-Hans/Repositories.md
  18. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs
  19. 34
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
  20. 17
      framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
  21. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
  22. 78
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs
  23. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs
  24. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs
  25. 11
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs
  26. 54
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs
  27. 14
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs
  28. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
  29. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs
  30. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs
  31. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
  32. 13
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs
  33. 21
      framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs
  34. 27
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  35. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
  36. 20
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs
  37. 38
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLogger.cs
  38. 15
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs
  39. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLogger.cs
  40. 7
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLoggerFactory.cs
  41. 15
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModuleHelper.cs
  42. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs
  43. 12
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs
  44. 8
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceExtensions.cs
  45. 7
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceList.cs
  46. 2
      framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj
  47. 4
      framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs
  48. 6
      framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs
  49. 15
      framework/src/Volo.Abp.Data/Volo/Abp/Data/ApplyDatabaseMigrationsEto.cs
  50. 6
      framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/ExceptionHandling/Localization/zh-Hans.json
  51. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  52. 32
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs
  53. 22
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs
  54. 11
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs
  55. 10
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/ModuleLoader_Tests.cs
  56. 128
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  57. 2012
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.Designer.cs
  58. 25
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.cs
  59. 2016
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.Designer.cs
  60. 24
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.cs
  61. 2020
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.Designer.cs
  62. 24
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.cs
  63. 1951
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210305122554_BlogPost_CoverImage.Designer.cs
  64. 56
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210305122554_BlogPost_CoverImage.cs
  65. 80
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  66. 6
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  67. 4
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj
  68. 7
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/tui-editor/tui-editor-jquery-patch.js
  69. 7
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs
  70. 5
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs
  71. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs
  72. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs
  73. 23
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentCreateDto.cs
  74. 15
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentDto.cs
  75. 13
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs
  76. 10
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListInput.cs
  77. 15
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentUpdateDto.cs
  78. 19
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs
  79. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/CreateMediaInputStream.cs
  80. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs
  81. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs
  82. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs
  83. 13
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs
  84. 13
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs
  85. 32
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs
  86. 31
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs
  87. 7
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs
  88. 88
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs
  89. 71
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs
  90. 42
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs
  91. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs
  92. 6
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs
  93. 44
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs
  94. 71
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs
  95. 8
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs
  96. 6
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs
  97. 29
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Create.cshtml
  98. 7
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Create.cshtml.cs
  99. 32
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Update.cshtml
  100. 7
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Update.cshtml.cs

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

@ -91,6 +91,7 @@
"UserNotFound": "User not found",
"{0}WillBeRemovedFromDevelopers": "{0} Will be removed from developers, do you confirm?",
"{0}WillBeRemovedFromOwners": "{0} Will be removed from owners, do you confirm?",
"{0}WillBeRemovedFromMembers": "{0} Will be removed from members, do you confirm?",
"Computers": "Computers",
"UniqueComputerId": "Unique computer id",
"LastSeenDate": "Last seen date",
@ -213,7 +214,26 @@
"Currency": "Currency",
"Gateway": "Gateway",
"State": "State",
"FailReason": "Fail reason"
"FailReason": "Fail reason",
"ReIndexAllPosts": "Reindex All Posts",
"ReIndexAllPostsConfirmationMessage": "Are you sure you want to reindex all posts?",
"SuccessfullyReIndexAllPosts": "All posts have been successfully reindexed.",
"Permission:FullSearch": "Full text search",
"Menu:CliAnalytics": "Cli Analytics",
"TemplateName": "Template name",
"TemplateVersion": "Template version",
"DatabaseProvider": "Database provider",
"IsTiered": "Is Tiered",
"ProjectName": "Project name",
"Username": "User name",
"Tool": "Tool",
"Command": "Command",
"UiFramework": "Ui framework",
"Options": "Options",
"CliAnalytics": "Cli Analytics",
"Permission:CliAnalyticses": "Cli Analyticses",
"Permission:CliAnalytics": "Cli Analytics",
"Search": "Search",
"ClearFilter": "Clear filter"
}
}

21
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json

@ -163,6 +163,25 @@
"Enum:ContentSource:2": "Video İçerik",
"DeleteCoverImage": "Kapak Fotoğrafını Sil",
"DeleteCoverImageConfirmationMessage": "Kapak fotoğrafını \"{0}\" isimli makale için silmek istediğinize emin misiniz?",
"DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir"
"DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir",
"ReIndexAllPosts": "Yeniden indeksle",
"ReIndexAllPostsConfirmationMessage": "Tüm gönderileri yeniden indeksleyeceğinizden emin misiniz?",
"SuccessfullyReIndexAllPosts": "Tüm gönderiler başarıyla yeniden indekslendi",
"Menu:CliAnalytics": "Cli Analitik",
"TemplateName": "Şablon ismi",
"TemplateVersion": "Şablon versiyon",
"DatabaseProvider": "Veritabanı sağlayıcı",
"IsTiered": "Katmanlı mı",
"ProjectName": "Proje ismi",
"Username": "Kullanıcı adı",
"Tool": "Araç",
"Command": "Komut",
"UiFramework": "Ui çerçevesi",
"Options": "Seçenekler",
"CliAnalytics": "Cli Analitik",
"Permission:CliAnalyticses": "Cli Analitikleri",
"Permission:CliAnalytics": "Cli Analitik",
"Search": "Ara",
"ClearFilter": "Filtreyi temizle"
}
}

25
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json

@ -35,6 +35,7 @@
"NameFilter": "名称",
"CreationTime": "创建时间",
"IsPro": "是否为专业版",
"IsFreeToActiveLicenseOwners": "许可证所有者免费",
"ShowOnModuleList": "展示模块列表",
"EfCoreConfigureMethodName": "配置方法法",
"IsProFilter": "是否为专业版",
@ -90,6 +91,7 @@
"UserNotFound": "用户不存在",
"{0}WillBeRemovedFromDevelopers": "{0} 将从开发者中移除, 你确定吗?",
"{0}WillBeRemovedFromOwners": "{0} 将从所有者中移除, 你确定吗?",
"{0}WillBeRemovedFromMembers": "{0} 将从成员中删除, 你确定吗?",
"Computers": "计算机",
"UniqueComputerId": "计算机唯一ID",
"LastSeenDate": "上次查看日期",
@ -194,6 +196,27 @@
"CoverImage": "封面图片",
"RemoveCacheConfirmationMessage": "你确定要删除\"{0}\" 文章缓存?",
"SuccessfullyRemoved": "清除成功",
"RemoveCache": "删除缓存"
"RemoveCache": "删除缓存",
"Language": "语言",
"Optional": "可选的",
"CreateArticleLanguageInfo": "发帖所用的语言",
"Enum:ContentSource:2": "视频发布",
"VideoPreview": "视频预览",
"VideoPreviewErrorMessage": "无法从YouTube获取给定的视频网址. 可能是由于视频是私有视频,或者给定的URL不可用.",
"DeleteCoverImage": "删除封面图片",
"DeleteCoverImageConfirmationMessage": "你确定要删除封面图片吗",
"DeleteCoverImageSuccessMessage": "成功删除封面图片",
"PaymentsOf": "付款的",
"ShowPaymentsOfOrganization": "显示付款",
"Date": "日期",
"Products": "商品",
"TotalAmount": "总金额",
"Currency": "货币",
"Gateway": "网关",
"State": "状态",
"FailReason": "失败原因",
"ReIndexAllPosts": "重新索引所有帖子",
"ReIndexAllPostsConfirmationMessage": "你确定要重新索引所有的帖子吗",
"SuccessfullyReIndexAllPosts": "成功索引所有的帖子"
}
}

5
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json

@ -136,6 +136,9 @@
"GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please be sure your Github username is correct.",
"PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please be sure your personal site URL is correct.",
"TwitterUserNameValidationMessage": "Your Twitter username can not include whitespace, please be sure your Twitter username is correct.",
"LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please be sure your Linkedin URL is correct."
"LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please be sure your Linkedin URL is correct.",
"NoPostsFound": "No posts found!",
"SearchInPosts": "Search in posts...",
"MinimumSearchContent": "You must enter at least 3 characters!"
}
}

31
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json

@ -110,7 +110,34 @@
"VideoContentForm": "视频内容来源",
"GithubPostForm": "Github文章来源",
"ExternalPostForm": "外部文章来源",
"PostSourceTypeChooses": "我们接受文章的三种来源类型;",
"Posts": "文章"
"HowToPost": "如何发布",
"Posts": "文章",
"VideoUrl": "视频Url",
"GithubArticleUrl": "Github文章Url",
"ExternalArticleUrl": "外部文章Url",
"CreatePostCoverInfo": "要创建一个有效的帖子,需要添加封面图片. 上传16:9的图片获取最佳视觉效果. 文件最大支持: 1MB",
"ThankYouForContribution": "感谢你对ABP社区的贡献",
"GithubArticle": "Github文章",
"GithubArticleSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> 用Markdown格式在GitHub的任何公共存储库上写一篇文章. <a target=\"_blank\" href=\"https://github.com/abpframework/abp/blob/dev/docs/en/Community-Articles/2020-12-04-Event-Organizer/Post.md\">示例</a>",
"GithubArticleSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> 使用表单提交你的文章URL.",
"GithubArticleSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> 你的文章将在这个网站中呈现.",
"YoutubeVideo": "Youtube视频",
"YoutubeVideoSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> 在YouTube上发布你的视频.",
"YoutubeVideoSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> 使用表单提交视频URL.",
"YoutubeVideoSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> 访客将能够在这个网站上直接观看你的视频内容.",
"ExternalContent": "外部内容",
"ExternalContentSubmitStepOne": "<span class=\"font-weight-bold\">1.</span> 在任何公开的平台创建内容(medium, 你自己的博客或者任何你喜欢的).",
"ExternalContentSubmitStepTwo": "<span class=\"font-weight-bold\">2.</span> 使用表单提交内容URL.",
"ExternalContentSubmitStepThree": "<span class=\"font-weight-bold\">3.</span> 访客被重定向到原网站的内容.",
"ChooseYourContentType": "请选择你想要添加内容的方式",
"PostContentViaGithub": "我想要使用<span class=\"font-weight-bold\"><i class=\"fa fa-github\"></i> GitHub</span>提交markdown格式的文章",
"PostContentViaYoutube": "我想要分享我在<span class=\"font-weight-bold\"><i class=\"fa fa-youtube\"></i> Youtube</span>的可用视频",
"PostContentViaExternalSource": "我想要添加我在<span class=\"font-weight-bold\">其他平台</span>发布的内容",
"GitHubUserNameValidationMessage": "您的Github用户名不能包含空格, 请确认你的Github用户名是正确的.",
"PersonalSiteUrlValidationMessage": "你的个人网站URL不能包含空格, 请确定你的个人网站URL是正确的.",
"TwitterUserNameValidationMessage": "你的Twitter用户名不能包含空格, 请确认你的Twitter用户名是正确的.",
"LinkedinUrlValidationMessage": "你的领英URL不能包含空格, 请确认你的领英URL是正确的",
"NoPostsFound": "没有发现帖子",
"SearchInPosts": "搜索帖子…"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json

@ -8,6 +8,7 @@
"ProjectName": "项目名称",
"ProjectType": "项目类型",
"DatabaseProvider": "数据库提供者",
"DatabaseManagementSystem": "数据库管理系统",
"NTier": "N层",
"IncludeUserInterface": "包含用户界面",
"CreateNow": "现在创建",
@ -194,7 +195,7 @@
"MultipleUIOptionsExplanation": "核心框架设计为独立与UI,可以和任何类型的UI系统一起使用. 同时提供了多个开箱即用的预构建集成选项.",
"MultipleDBOptionsExplanation": "该框架可以使用任何数据源,并且以下提供程序已得到正式开发和支持;",
"SelectLanguage": "选择语言",
"LatestArticleOnCommunity": "关于ABP社区的最新文章",
"Register": "寄存器"
"LatestArticleOnCommunity": "关于ABP社区的最新文章",
"Register": "寄存器"
}
}
}

20
docs/en/Background-Jobs-RabbitMq.md

@ -66,6 +66,26 @@ Defining multiple connections is allowed. In this case, you can use different co
}
````
If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names.
**Example: Connect to the RabbitMQ cluster**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123;234.234.234.234"
}
},
"EventBus": {
"ClientName": "MyClientName",
"ExchangeName": "MyExchangeName"
}
}
}
```
#### AbpRabbitMqOptions
`AbpRabbitMqOptions` class can be used to configure the connection strings for the RabbitMQ. You can configure this options inside the `ConfigureServices` of your [module](Module-Development-Basics.md).

2
docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md

@ -78,7 +78,7 @@ public static class CustomizeUserDemoEfCoreEntityExtensionMappings
nameof(AppUser.Title),
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.IsRequired();
propertyBuilder.HasDefaultValue();
propertyBuilder.HasMaxLength(UserConsts.MaxTitleLength);
}
).MapEfCoreProperty<IdentityUser, int>(

22
docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md

@ -90,7 +90,7 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot
**Example: Specify the connection port**
````csharp
````json
{
"RabbitMQ": {
"Connections": {
@ -103,6 +103,26 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot
}
````
If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names.
**Example: Connect to the RabbitMQ cluster**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123;234.234.234.234"
}
},
"EventBus": {
"ClientName": "MyClientName",
"ExchangeName": "MyExchangeName"
}
}
}
```
### The Options Classes
`AbpRabbitMqOptions` and `AbpRabbitMqEventBusOptions` classes can be used to configure the connection strings and event bus options for the RabbitMQ.

101
docs/en/UI/Angular/HTTP-Requests.md

@ -27,13 +27,13 @@ An `HttpInterceptor` is able to catch `HttpErrorResponse`  and can be used for
## RestService
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by deafult.
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by default.
### Getting Started with RestService
In order to use the `RestService`, you must inject it in your class as a dependency.
In order to use the `RestService`, you must inject it in your class as a dependency.
```js
import { RestService } from '@abp/ng.core';
@ -200,6 +200,101 @@ getSomeCustomHeaderValue() {
}
```
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
## HTTP Error Handling
When the `RestService` is used, all HTTP errors are automatically handled by `ErrorHandler` which is a service that exposed by the `@abp/ng.theme.shared` package.
### Custom HTTP Error Handler
A custom HTTP error handler can be registered to an injection token named `HTTP_ERROR_HANDLER`. If a custom handler function is registered, the `ErrorHandler` executes that function.
See an example:
```js
// http-error-handler.ts
import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { HttpErrorResponse } from '@angular/common/http';
import { Injector } from '@angular/core';
import { throwError } from 'rxjs';
import { Error404Component } from './error404/error404.component';
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 400) {
const toaster = injector.get(ToasterService);
toaster.error(httpError.error?.error?.message || 'Bad request!', '400');
return;
}
if (httpError.status === 404) {
const contentProjection = injector.get(ContentProjectionService);
contentProjection.projectContent(PROJECTION_STRATEGY.AppendComponentToBody(Error404Component));
return;
}
return throwError(httpError);
}
// app.module.ts
import { Error404Component } from './error404/error404.component';
import { handleHttpErrors } from './http-error-handling';
import { HTTP_ERROR_HANDLER, ... } from '@abp/ng.theme.shared';
@NgModule({
// ...
providers: [
// ...
{ provide: HTTP_ERROR_HANDLER, useValue: handleHttpErrors }
],
declarations: [
//...
Error404Component],
})
export class AppModule {}
```
In the example above:
- Created a function named `handleHttpErrors` and defined as value of the `HTTP_ERROR_HANDLER` provider in app.module. After this, the function executes when an HTTP error occurs.
- 400 bad request errors is handled. When a 400 error occurs, backend error message will be displayed as shown below:
![custom-error-handler-toaster-message](images/custom-error-handler-toaster-message.jpg)
- 404 not found errors is handled. When a 404 error occurs, `Error404Component` will be appended to the `<body>` as shown below:
![custom-error-handler-404-component](images/custom-error-handler-404-component.jpg)
- Since `throwError(httpError)` is returned at bottom of the `handleHttpErrors`, the `ErrorHandler` will handle the HTTP errors except 400 and 404 errors.
**Note 1:** If you put `return` to next line of handling an error, default error handling will not work for that error.
```js
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 403) {
// handle 403 errors here
return; // put return to skip default error handling
}
}
```
**Note 2:** If you put `return throwError(httpError)`, default error handling will work.
- `throwError` is a function. It can be imported from `rxjs`.
- `httpError` is the second parameter of the error handler function which is registered to the `HTTP_ERROR_HANDLER` provider. Type of the `httpError` is `HttpErrorResponse`.
```js
import { throwError } from 'rxjs';
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
if (httpError.status === 500) {
// handle 500 errors here
return;
}
// you can return the throwError(httpError) at bottom of the function to run the default handler of ABP for HTTP errors that you didn't handle above.
return throwError(httpError)
}
```

2
docs/en/UI/Angular/Router-Events.md

@ -1,6 +1,6 @@
# Router Events Simplified
`RouterEvents` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose.
`RouterEvents` is a utility service for filtering specific router events and reacting to them. Please see [this page in Angular docs](https://angular.io/api/router/Event) for available router events.

4
docs/en/UI/Angular/Service-Proxies.md

@ -137,3 +137,7 @@ export class BookComponent implements OnInit {
```
> Please [see this article](https://github.com/abpframework/abp/blob/dev/docs/en/Blog-Posts/2020-09-07%20Angular-Service-Proxies/POST.md) to learn more about service proxies.
### Known Limitations
When you run a project on Visual Studio using IIS Express as the web server, there will be no remote access to your endpoints. This is the default behavior of IIS Express since it explicitly protects you from the security risks of running over the network. However, that will cause the proxy generator to fail because it needs a response from the `/api/abp/api-definition` endpoint. You may serve your endpoints via Kestrel to avoid this. Running `dotnet run` in your command line (at your project folder) will do that for you.

BIN
docs/en/UI/Angular/images/custom-error-handler-404-component.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
docs/en/UI/Angular/images/custom-error-handler-toaster-message.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

154
docs/zh-Hans/Background-Jobs-RabbitMq.md

@ -1,3 +1,153 @@
# RabbitMQ Background Job Manager
# RabbitMQ 后台作业管理
待添加
RabbitMQ 是一个标准的消息队列中间件,虽然它常用于消息传递/分布式事件,但也非常适合存储 FIFO(先进先出) 顺序的后台作业.
ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包,将使用 RabbitMQ 来执行后台作业.
> 参阅 [后台作业文档](Background-Jobs.md) 学习如何使用后台作业系统,本文只介绍了如何安装和配置 RabbitMQ 集成.
## 安装
使用 ABP CLI 将 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包添加到你的项目:
- 如果之前没有安装过 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI),请先安装它.
- 跳转到待安装后台作业管理的项目目录中(包含 `.csproj` 文件的目录),打开终端管理器.
- 执行 `abp add-package Volo.Abp.BackgroundJobs.RabbitMQ` 命令.
如果你想要手动安装,请先用 NuGet 包管理器安装 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包到指定项目,之后使在你的 [模块](Module-Development-Basics.md) 上面添加 `[DependsOn(typeof(AbpBackgroundJobsRabbitMqModule))]` 配置依赖.
## 配置
### 默认配置
默认配置将会使用标准端口和主机名(localhost)连接到 RabbitMQ 服务,**你不需要进行额外配置**.
### RabbitMQ 连接
你可以使用 ASP.NET Core 的 [标准配置系统](Configuration.md) 对 RabbitMQ 进行详细配置,比如 `appsettings.json` 或者是 [选项类](Options.md).
#### 通过 `appsettings.json` 文件配置
这种方式是配置 RabbitMQ 连接最简单的方式,你可以使用其他的配置源(例如环境变量).这些强大的功能都是由 [ASP.NET Core](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/) 提供的支持.
**示例: 配置默认的 RabbitMQ 连接**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123",
"Port": "5672"
}
}
}
}
```
你可以在配置文件使用所有 [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) 的属性,关于这些属性的具体含义,可以查看 RabbitMQ 的 [官方文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues).
目前我们允许定义多个连接,多连接的情况适用于不同的后台作业,具体配置信息可以参考下面的 RabbitMQ 后台作业配置说明.
**示例: 定义两个 RabbitMQ 连接**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123"
},
"SecondConnection": {
"HostName": "321.321.321.321"
}
}
}
}
```
如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName.
**示例: 连接到 RabbitMQ 集群**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123;234.234.234.234"
}
},
"EventBus": {
"ClientName": "MyClientName",
"ExchangeName": "MyExchangeName"
}
}
}
```
#### 使用选项类
`AbpRabbitMqOptions` 类型用于配置 RabbitMQ 的连接字符串,你可以在 [模块](Module-Development-Basics.md) 的 `ConfigureService` 方法中进行配置.
**示例: 配置 RabbitMQ 连接**
```csharp
Configure<AbpRabbitMqOptions>(options =>
{
options.Connections.Default.UserName = "user";
options.Connections.Default.Password = "pass";
options.Connections.Default.HostName = "123.123.123.123";
options.Connections.Default.Port = 5672;
});
```
关于选项类,可以结合 `appsettings.json` 文件一起使用.针对同一个属性,在选项类里面对该值进行了设定,会覆盖掉 `appsettings.json` 的值.
### RabbitMQ 后台作业配置说明
#### 后台作业队列的名称
默认情况下,每个后台作业都会使用一个单独的队列,结合标准前缀和作业名称来构造一个完整的队列名称.默认的前缀为 `AbpBackgroundJobs`,所以有一个作业的名称是 `EmailSending` 的话,在 RabbitMQ 的队列名称就是 `AbpBackgroundJobs.EmailSending`.
> 在后台作业的参数类上,可以使用 `BackgroundJobName` 特性指定后台作业的名称.否则的话,后台作业的名称将会是后台作业类的全名(也包含命名空间).
#### 后台作业使用的连接
默认情况下,后台作业都会使用 `Default` 作为默认连接.
#### 自定义
`AbpRabbitMqBackgroundJobOptions` 可以自定义队列名和作业使用的 RabbitMQ 连接.
**示例: **
```csharp
Configure<AbpRabbitMqBackgroundJobOptions>(options =>
{
options.DefaultQueueNamePrefix = "my_app_jobs.";
options.JobQueues[typeof(EmailSendingArgs)] =
new JobQueueConfiguration(
typeof(EmailSendingArgs),
queueName: "my_app_jobs.emails",
connectionName: "SecondConnection"
);
});
```
- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业.
- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接.
`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数.
- `queueName`: 指定后台作业对应的队列名称(全名).
- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`.
- `durable`: 可选参数,默认为 `true`.
- `exclusive`: 可选参数,默认为 `false`.
- `autoDelete`: 可选参数,默认为 `false`.
如果你想要更多地了解 `durable`,`exclusive`,`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档.
## 另请参阅
- [后台作业](Background-Jobs.md)

20
docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md

@ -103,6 +103,26 @@
}
````
如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName。
**示例: 连接到 RabbitMQ 集群**
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "123.123.123.123;234.234.234.234"
}
},
"EventBus": {
"ClientName": "MyClientName",
"ExchangeName": "MyExchangeName"
}
}
}
```
### 选项类
`AbpRabbitMqOptions` 和 `AbpRabbitMqEventBusOptions` 类用于配置RabbitMQ的连接字符串和事件总线选项.

2
docs/zh-Hans/Repositories.md

@ -150,7 +150,7 @@ var people = _personRepository
* 这里异步方法**不是标准LINQ方法**,它们定义在[Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore)Nuget包中.
* 标准模板应用层与领域层**不引用**EF Core 包以实现数据库提供程序独立.
根据你的需求和开发模式,你可以根据以下选项使用异步方法.s
根据你的需求和开发模式,你可以根据以下选项使用异步方法.
> 强烈建议使用异步方法! 在执行数据库查询时不要使用同步LINQ方法,以便能够开发可伸缩的应用程序.

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs

@ -16,6 +16,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/tui-editor/tui-editor-jquery-patch.js");
context.Files.AddIfNotContains("/libs/to-mark/to-mark.min.js");
if (context.FileProvider.GetFileInfo("/libs/tui-code-snippet/tui-code-snippet.min.js").Exists)

34
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs

@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
@ -19,7 +20,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
public AbpRemoteServiceApiDescriptionProvider(
IModelMetadataProvider modelMetadataProvider,
IOptions<MvcOptions> mvcOptionsAccessor,
IOptions<MvcOptions> mvcOptionsAccessor,
IOptions<AbpRemoteServiceApiDescriptionProviderOptions> optionsAccessor)
{
_modelMetadataProvider = modelMetadataProvider;
@ -41,7 +42,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
{
foreach (var apiResponseType in GetApiResponseTypes())
{
foreach (var result in context.Results.Where(x => IsRemoteService(x.ActionDescriptor)))
foreach (var result in context.Results.Where(IsRemoteService))
{
var actionProducesResponseTypeAttributes =
ReflectionHelper.GetAttributesOfMemberOrDeclaringType<ProducesResponseTypeAttribute>(
@ -62,7 +63,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
foreach (var apiResponse in _options.SupportedResponseTypes)
{
apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type);
foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>())
{
var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, apiResponse.Type);
@ -85,10 +86,29 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
return _options.SupportedResponseTypes;
}
protected virtual bool IsRemoteService(ActionDescriptor actionDescriptor)
protected virtual bool IsRemoteService(ApiDescription actionDescriptor)
{
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(actionDescriptor.GetMethodInfo());
return remoteServiceAttr != null && remoteServiceAttr.IsEnabled;
if (actionDescriptor.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.MethodInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.ControllerTypeInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
if (typeof(IRemoteService).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo))
{
return true;
}
}
return false;
}
}
}
}

17
framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs

@ -65,6 +65,23 @@ namespace Microsoft.AspNetCore.Authorization
{
return (await authorizationService.AuthorizeAsync(policyName)).Succeeded;
}
public static async Task<bool> IsGrantedAnyAsync(
this IAuthorizationService authorizationService,
params string[] policyNames)
{
Check.NotNullOrEmpty(policyNames, nameof(policyNames));
foreach (var policyName in policyNames)
{
if ((await authorizationService.AuthorizeAsync(policyName)).Succeeded)
{
return true;
}
}
return false;
}
public static async Task<bool> IsGrantedAsync(this IAuthorizationService authorizationService, object resource, IAuthorizationRequirement requirement)
{

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs

@ -1,4 +1,5 @@
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
using Volo.Abp.Cli.ProjectBuilding.Templates;
namespace Volo.Abp.Cli.ProjectBuilding.Building
{
@ -12,6 +13,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
pipeline.Steps.Add(new ProjectReferenceReplaceStep());
pipeline.Steps.Add(new ReplaceCommonPropsStep());
pipeline.Steps.Add(new ReplaceConfigureAwaitPropsStep());
pipeline.Steps.Add(new UpdateNuGetConfigStep("/NuGet.Config"));
pipeline.Steps.Add(new CreateProjectResultZipStep());
return pipeline;

78
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs

@ -0,0 +1,78 @@
using System;
using System.IO;
using System.Linq;
using Volo.Abp.Cli.Commands;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public class MicroserviceServiceRandomPortStep : ProjectBuildPipelineStep
{
private readonly string _defaultPort = string.Empty;
private string _tyeFileContent = null;
public MicroserviceServiceRandomPortStep(string defaultPort)
{
_defaultPort = defaultPort;
}
public override void Execute(ProjectBuildContext context)
{
var newPort = GetNewRandomPort(context);
var targetFiles = context.Files.Where(f=> f.Name.EndsWith("launchSettings.json") || f.Name.EndsWith("appsettings.json")).ToList();
foreach (var file in targetFiles)
{
file.SetContent(file.Content.Replace(_defaultPort, newPort));
}
}
private string GetNewRandomPort(ProjectBuildContext context)
{
string newPort;
var rnd = new Random();
var tryCount = 0;
do
{
newPort = rnd.Next(44350, 45350).ToString();
if (tryCount++ > 2000)
{
break;
}
} while (PortExistsForAnotherService(context, newPort));
return newPort;
}
private bool PortExistsForAnotherService(ProjectBuildContext context, string newPort)
{
return ReadTyeFileContent(context).SplitToLines().Any(l => l.Contains("port") && l.Contains(newPort));
}
private string ReadTyeFileContent(ProjectBuildContext context)
{
if (_tyeFileContent != null)
{
return _tyeFileContent;
}
var solutionFolderPath = context.BuildArgs.ExtraProperties[NewCommand.Options.OutputFolder.Short] ??
context.BuildArgs.ExtraProperties[NewCommand.Options.OutputFolder.Long] ??
Directory.GetCurrentDirectory();
var tyeFilePath = Path.Combine(solutionFolderPath, "tye.yaml");
if (!File.Exists(tyeFilePath))
{
return String.Empty;
}
_tyeFileContent = File.ReadAllText(tyeFilePath);
return _tyeFileContent;
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs

@ -9,12 +9,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
var commonFiles = context.Files.Where(f =>
f.Name.EndsWith(".csproj") ||
f.Name.EndsWith("Module.cs") ||
f.Name.EndsWith("MyProjectNameMigrationsDbContext.cs") ||
f.Name.EndsWith("MyProjectNameMigrationsDbContextBase.cs") ||
f.Name.EndsWith("MyProjectNameGlobalFeatureConfigurator.cs") ||
(f.Name.EndsWith(".cshtml") && f.Name.Contains("MyCompanyName.MyProjectName.Web.Public"))
);
f.Name.EndsWith(".cs") ||
f.Name.EndsWith(".json") ||
f.Name.EndsWith(".cshtml"));
foreach (var file in commonFiles)
{

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs

@ -7,8 +7,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Web.Public.csproj"))?.RemoveTemplateCodeIfNot("EFCORE");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameWebPublicModule.cs"))?.RemoveTemplateCodeIfNot("EFCORE");
foreach (var file in context.Files)
{
if (file.Name.EndsWith(".cs") || file.Name.EndsWith(".csproj") || file.Name.EndsWith(".json"))
{
file.RemoveTemplateCodeIfNot("EFCORE");
}
}
}
}
}

11
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs

@ -7,8 +7,15 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Domain.Shared.csproj"))?.RemoveTemplateCodeIf("CMS-KIT");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameDomainSharedModule.cs"))?.RemoveTemplateCodeIf("CMS-KIT");
var commonFiles = context.Files.Where(f =>
f.Name.EndsWith(".csproj") ||
f.Name.EndsWith(".cs") ||
f.Name.EndsWith(".cshtml"));
foreach (var file in commonFiles)
{
file.RemoveTemplateCodeIf("CMS-KIT");
}
}
}
}

54
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public class RemoveProjectFromTyeStep : ProjectBuildPipelineStep
{
private readonly string _name;
public RemoveProjectFromTyeStep(string name)
{
_name = name;
}
public override void Execute(ProjectBuildContext context)
{
var tyeFile = context.Files.FirstOrDefault(f => f.Name == "/tye.yaml");
if (tyeFile == null)
{
return;
}
var lines = tyeFile.GetLines();
var newLines = new List<string>();
var nameLine = $"- name:";
var isOneOfTargetLines = false;
foreach (var line in lines)
{
if (line.Equals($"{nameLine} {_name}"))
{
isOneOfTargetLines = true;
continue;
}
if (line.StartsWith(nameLine))
{
isOneOfTargetLines = false;
}
if (!isOneOfTargetLines)
{
newLines.Add(line);
}
}
tyeFile.SetContent(String.Join(Environment.NewLine, newLines));
}
}
}

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

@ -8,16 +8,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Web.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameWebModule.cs"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.HttpApi.Host.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameHttpApiHostModule.cs"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
var appSettingsFiles = context.Files.Where(f => f.Name.EndsWith("appSettings.json", StringComparison.InvariantCultureIgnoreCase)).ToList();
foreach (var appSettings in appSettingsFiles)
foreach (var file in context.Files)
{
appSettings.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
if (file.Name.EndsWith(".cs") || file.Name.EndsWith(".csproj") || file.Name.EndsWith(".json"))
{
file.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
}
}
}
}

8
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs

@ -18,6 +18,14 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
context.BuildArgs.SolutionName.CompanyName,
context.BuildArgs.SolutionName.ProjectName
).Run();
new SolutionRenamer(
context.Files,
null,
"MyProjectName",
null,
SolutionName.Parse(context.BuildArgs.SolutionName.CompanyName).ProjectName
).Run();
}
else
{

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs

@ -28,6 +28,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Files
public static void RemoveTemplateCodeMarkers(this FileEntry file)
{
if (!file.Content.Contains("</TEMPLATE-REMOVE>"))
{
return;
}
file.NormalizeLineEndings();
var lines = file.GetLines();
@ -59,6 +64,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Files
private static void RemoveMarkedTemplateCode(this FileEntry file, string beginMark)
{
if (!file.Content.Contains("</TEMPLATE-REMOVE>"))
{
return;
}
file.NormalizeLineEndings();
var lines = file.GetLines();

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs

@ -31,6 +31,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
var steps = new List<ProjectBuildPipelineStep>();
DeleteUnrelatedUiProject(context, steps);
SetRandomPortForHostProject(context, steps);
RandomizeStringEncryption(context, steps);
return steps;
@ -54,6 +55,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
}
}
private static void SetRandomPortForHostProject(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new MicroserviceServiceRandomPortStep("44371"));
}
private static void RandomizeStringEncryption(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new RandomizeStringEncryptionStep());

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs

@ -36,9 +36,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web",null,
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveProjectFromTyeStep("web"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
steps.Add(new RemoveProjectFromTyeStep("blazor"));
steps.Add(new RemoveFolderStep("/angular"));
break;
@ -46,9 +50,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web",null,
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveProjectFromTyeStep("web"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
steps.Add(new RemoveProjectFromTyeStep("blazor"));
break;
case UiFramework.Blazor:
@ -56,6 +63,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveFolderStep("/angular"));
steps.Add(new RemoveProjectFromTyeStep("web"));
break;
case UiFramework.Mvc:
@ -63,6 +71,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
steps.Add(new RemoveProjectFromTyeStep("blazor"));
steps.Add(new RemoveFolderStep("/angular"));
break;
}

13
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs

@ -0,0 +1,13 @@
using Microsoft.Extensions.Logging;
using Volo.Abp.Logging;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionLoggingExtensions
{
public static ILogger<T> GetInitLogger<T>(this IServiceCollection services)
{
return services.GetSingletonInstance<IInitLoggerFactory>().Create<T>();
}
}
}

21
framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs

@ -260,8 +260,9 @@ namespace System
/// </summary>
/// <param name="str">String to convert</param>
/// <param name="useCurrentCulture">set true to use current culture. Otherwise, invariant culture will be used.</param>
/// <param name="handleAbbreviations">set true to if you want to convert 'XYZ' to 'xyz'.</param>
/// <returns>camelCase of the string</returns>
public static string ToCamelCase(this string str, bool useCurrentCulture = false)
public static string ToCamelCase(this string str, bool useCurrentCulture = false, bool handleAbbreviations = false)
{
if (string.IsNullOrWhiteSpace(str))
{
@ -273,6 +274,11 @@ namespace System
return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
}
if (handleAbbreviations && IsAllUpperCase(str))
{
return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
}
return (useCurrentCulture ? char.ToLower(str[0]) : char.ToLowerInvariant(str[0])) + str.Substring(1);
}
@ -545,5 +551,18 @@ namespace System
return encoding.GetBytes(str);
}
private static bool IsAllUpperCase(string input)
{
for (int i = 0; i < input.Length; i++)
{
if (Char.IsLetter(input[i]) && !Char.IsUpper(input[i]))
{
return false;
}
}
return true;
}
}
}

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

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal;
using Volo.Abp.Logging;
using Volo.Abp.Modularity;
namespace Volo.Abp
@ -60,7 +62,7 @@ namespace Volo.Abp
{
//TODO: Shutdown if not done before?
}
protected virtual void SetServiceProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
@ -71,12 +73,31 @@ namespace Volo.Abp
{
using (var scope = ServiceProvider.CreateScope())
{
WriteInitLogs(scope.ServiceProvider);
scope.ServiceProvider
.GetRequiredService<IModuleManager>()
.InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
}
}
protected virtual void WriteInitLogs(IServiceProvider serviceProvider)
{
var logger = serviceProvider.GetService<ILogger<AbpApplicationBase>>();
if (logger == null)
{
return;
}
var initLogger = serviceProvider.GetRequiredService<IInitLoggerFactory>().Create<AbpApplicationBase>();
foreach (var entry in initLogger.Entries)
{
logger.Log(entry.LogLevel, entry.EventId, entry.State, entry.Exception, entry.Formatter);
}
initLogger.Entries.Clear();
}
protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{
return services
@ -87,7 +108,7 @@ namespace Volo.Abp
options.PlugInSources
);
}
//TODO: We can extract a new class for this
protected virtual void ConfigureServices()
{
@ -158,4 +179,4 @@ namespace Volo.Abp
}
}
}
}
}

6
framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs

@ -1,6 +1,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Logging;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
@ -16,7 +17,7 @@ namespace Volo.Abp.Internal
}
internal static void AddCoreAbpServices(this IServiceCollection services,
IAbpApplication abpApplication,
IAbpApplication abpApplication,
AbpApplicationCreationOptions applicationCreationOptions)
{
var moduleLoader = new ModuleLoader();
@ -35,6 +36,7 @@ namespace Volo.Abp.Internal
services.TryAddSingleton<IModuleLoader>(moduleLoader);
services.TryAddSingleton<IAssemblyFinder>(assemblyFinder);
services.TryAddSingleton<ITypeFinder>(typeFinder);
services.TryAddSingleton<IInitLoggerFactory>(new DefaultInitLoggerFactory());
services.AddAssemblyOf<IAbpApplication>();
@ -47,4 +49,4 @@ namespace Volo.Abp.Internal
});
}
}
}
}

20
framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs

@ -0,0 +1,20 @@
using System;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public class AbpInitLogEntry
{
public LogLevel LogLevel { get; set; }
public EventId EventId { get; set; }
public object State { get; set; }
public Exception Exception { get; set; }
public Func<object, Exception, string> Formatter { get; set; }
public string Message => Formatter(State, Exception);
}
}

38
framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLogger.cs

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public class DefaultInitLogger<T> : IInitLogger<T>
{
public List<AbpInitLogEntry> Entries { get; }
public DefaultInitLogger()
{
Entries = new List<AbpInitLogEntry>();
}
public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Entries.Add(new AbpInitLogEntry
{
LogLevel = logLevel,
EventId = eventId,
State = state,
Exception = exception,
Formatter = (s, e) => formatter((TState)s, e),
});
}
public virtual bool IsEnabled(LogLevel logLevel)
{
return logLevel != LogLevel.None;
}
public virtual IDisposable BeginScope<TState>(TState state)
{
return NullDisposable.Instance;
}
}
}

15
framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Logging
{
public class DefaultInitLoggerFactory : IInitLoggerFactory
{
private readonly Dictionary<Type, object> _cache = new Dictionary<Type, object>();
public virtual IInitLogger<T> Create<T>()
{
return (IInitLogger<T>)_cache.GetOrAdd(typeof(T), () => new DefaultInitLogger<T>());;
}
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLogger.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public interface IInitLogger<out T> : ILogger<T>
{
public List<AbpInitLogEntry> Entries { get; }
}
}

7
framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLoggerFactory.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Logging
{
public interface IInitLoggerFactory
{
IInitLogger<T> Create<T>();
}
}

15
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModuleHelper.cs

@ -2,15 +2,17 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Modularity
{
internal static class AbpModuleHelper
{
public static List<Type> FindAllModuleTypes(Type startupModuleType)
public static List<Type> FindAllModuleTypes(Type startupModuleType, ILogger logger)
{
var moduleTypes = new List<Type>();
AddModuleAndDependenciesResursively(moduleTypes, startupModuleType);
logger.Log(LogLevel.Information, "Loaded ABP modules:");
AddModuleAndDependenciesResursively(moduleTypes, startupModuleType, logger);
return moduleTypes;
}
@ -35,7 +37,11 @@ namespace Volo.Abp.Modularity
return dependencies;
}
private static void AddModuleAndDependenciesResursively(List<Type> moduleTypes, Type moduleType)
private static void AddModuleAndDependenciesResursively(
List<Type> moduleTypes,
Type moduleType,
ILogger logger,
int depth = 0)
{
AbpModule.CheckAbpModuleType(moduleType);
@ -45,10 +51,11 @@ namespace Volo.Abp.Modularity
}
moduleTypes.Add(moduleType);
logger.Log(LogLevel.Information, $"{new string(' ', depth * 2)}- {moduleType.FullName}");
foreach (var dependedModuleType in FindDependedModuleTypes(moduleType))
{
AddModuleAndDependenciesResursively(moduleTypes, dependedModuleType);
AddModuleAndDependenciesResursively(moduleTypes, dependedModuleType, logger, depth + 1);
}
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs

@ -25,7 +25,7 @@ namespace Volo.Abp.Modularity
}
private List<IAbpModuleDescriptor> GetDescriptors(
IServiceCollection services,
IServiceCollection services,
Type startupModuleType,
PlugInSourceList plugInSources)
{
@ -43,14 +43,16 @@ namespace Volo.Abp.Modularity
Type startupModuleType,
PlugInSourceList plugInSources)
{
var logger = services.GetInitLogger<AbpApplicationBase>();
//All modules starting from the startup module
foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType))
foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType, logger))
{
modules.Add(CreateModuleDescriptor(services, moduleType));
}
//Plugin modules
foreach (var moduleType in plugInSources.GetAllModules())
foreach (var moduleType in plugInSources.GetAllModules(logger))
{
if (modules.Any(m => m.Type == moduleType))
{
@ -102,4 +104,4 @@ namespace Volo.Abp.Modularity
}
}
}
}
}

12
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs

@ -32,8 +32,6 @@ namespace Volo.Abp.Modularity
public void InitializeModules(ApplicationInitializationContext context)
{
LogListOfModules();
foreach (var contributor in _lifecycleContributors)
{
foreach (var module in _moduleContainer.Modules)
@ -52,16 +50,6 @@ namespace Volo.Abp.Modularity
_logger.LogInformation("Initialized all ABP modules.");
}
private void LogListOfModules()
{
_logger.LogInformation("Loaded ABP modules:");
foreach (var module in _moduleContainer.Modules)
{
_logger.LogInformation("- " + module.Type.FullName);
}
}
public void ShutdownModules(ApplicationShutdownContext context)
{
var modules = _moduleContainer.Modules.Reverse().ToList();

8
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceExtensions.cs

@ -1,21 +1,23 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Volo.Abp.Logging;
namespace Volo.Abp.Modularity.PlugIns
{
public static class PlugInSourceExtensions
{
[NotNull]
public static Type[] GetModulesWithAllDependencies([NotNull] this IPlugInSource plugInSource)
public static Type[] GetModulesWithAllDependencies([NotNull] this IPlugInSource plugInSource, ILogger logger)
{
Check.NotNull(plugInSource, nameof(plugInSource));
return plugInSource
.GetModules()
.SelectMany(AbpModuleHelper.FindAllModuleTypes)
.SelectMany(type => AbpModuleHelper.FindAllModuleTypes(type, logger))
.Distinct()
.ToArray();
}
}
}
}

7
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceList.cs

@ -2,18 +2,19 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Modularity.PlugIns
{
public class PlugInSourceList : List<IPlugInSource>
{
[NotNull]
internal Type[] GetAllModules()
internal Type[] GetAllModules(ILogger logger)
{
return this
.SelectMany(pluginSource => pluginSource.GetModulesWithAllDependencies())
.SelectMany(pluginSource => pluginSource.GetModulesWithAllDependencies(logger))
.Distinct()
.ToArray();
}
}
}
}

2
framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus.Abstractions\Volo.Abp.EventBus.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj" />
<ProjectReference Include="..\Volo.Abp.Uow\Volo.Abp.Uow.csproj" />
</ItemGroup>

4
framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.EventBus.Abstractions;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Uow;
@ -10,7 +11,8 @@ namespace Volo.Abp.Data
{
[DependsOn(
typeof(AbpObjectExtendingModule),
typeof(AbpUnitOfWorkModule)
typeof(AbpUnitOfWorkModule),
typeof(AbpEventBusAbstractionsModule)
)]
public class AbpDataModule : AbpModule
{

6
framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs

@ -4,7 +4,7 @@ namespace Volo.Abp.Data
{
public class AbpDatabaseInfo
{
public string DatabaseName { get; set; }
public string DatabaseName { get; }
/// <summary>
/// List of connection names mapped to this database.
@ -12,8 +12,8 @@ namespace Volo.Abp.Data
public HashSet<string> MappedConnections { get; }
/// <summary>
/// Is this database used by tenants. Set this to true if this database
/// can't owned by tenants.
/// Is this database used by tenants. Set this to false if this database
/// can not owned by tenants.
///
/// Default: true.
/// </summary>

15
framework/src/Volo.Abp.Data/Volo/Abp/Data/ApplyDatabaseMigrationsEto.cs

@ -0,0 +1,15 @@
using System;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace Volo.Abp.Data
{
[Serializable]
[EventName("abp.data.apply_database_migrations")]
public class ApplyDatabaseMigrationsEto : EtoBase
{
public Guid? TenantId { get; set; }
public string DatabaseName { get; set; }
}
}

6
framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/ExceptionHandling/Localization/zh-Hans.json

@ -18,6 +18,8 @@
"401Message": "未授权",
"403Message": "禁止访问",
"404Message": "网页未找到",
"500Message": "内部服务器错误"
"500Message": "内部服务器错误",
"403MessageDetail": "您没有权限执行此操作",
"404MessageDetail": "抱歉, 这个地址是空的"
}
}
}

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs

@ -24,7 +24,7 @@ namespace Volo.Abp.Http.Modeling
return new PropertyApiDescriptionModel
{
Name = propertyInfo.Name,
JsonName = AbpApiProxyScriptingOptions.PropertyNameGenerator.Invoke(propertyInfo),
JsonName = AbpApiProxyScriptingConfiguration.PropertyNameGenerator.Invoke(propertyInfo),
Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType),
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType),
IsRequired = propertyInfo.IsDefined(typeof(RequiredAttribute), true)

32
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs

@ -0,0 +1,32 @@
using System;
using System.Reflection;
namespace Volo.Abp.Http.ProxyScripting.Configuration
{
public static class AbpApiProxyScriptingConfiguration
{
public static Func<PropertyInfo, string> PropertyNameGenerator { get; set; }
static AbpApiProxyScriptingConfiguration()
{
PropertyNameGenerator = propertyInfo =>
{
var jsonPropertyNameAttribute = propertyInfo.GetSingleAttributeOrNull<System.Text.Json.Serialization.JsonPropertyNameAttribute>(true);
if (jsonPropertyNameAttribute != null)
{
return jsonPropertyNameAttribute.Name;
}
var jsonPropertyAttribute = propertyInfo.GetSingleAttributeOrNull<Newtonsoft.Json.JsonPropertyAttribute>(true);
if (jsonPropertyAttribute != null)
{
return jsonPropertyAttribute.PropertyName;
}
return null;
};
}
}
}

22
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Volo.Abp.Http.ProxyScripting.Configuration
{
@ -8,30 +7,9 @@ namespace Volo.Abp.Http.ProxyScripting.Configuration
{
public IDictionary<string, Type> Generators { get; }
public static Func<PropertyInfo, string> PropertyNameGenerator { get; set; }
public AbpApiProxyScriptingOptions()
{
Generators = new Dictionary<string, Type>();
PropertyNameGenerator = propertyInfo =>
{
var jsonPropertyNameAttribute = propertyInfo.GetSingleAttributeOrNull<System.Text.Json.Serialization.JsonPropertyNameAttribute>(true);
if (jsonPropertyNameAttribute != null)
{
return jsonPropertyNameAttribute.Name;
}
var jsonPropertyAttribute = propertyInfo.GetSingleAttributeOrNull<Newtonsoft.Json.JsonPropertyAttribute>(true);
if (jsonPropertyAttribute != null)
{
return jsonPropertyAttribute.PropertyName;
}
return null;
};
}
}
}

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

@ -26,10 +26,13 @@ namespace Volo.Abp.RabbitMQ
return Connections.GetOrAdd(
connectionName,
() => Options
.Connections
.GetOrDefault(connectionName)
.CreateConnection()
() =>
{
var connection = Options.Connections.GetOrDefault(connectionName);
var hostnames = connection.HostName.TrimEnd(';').Split(';');
// Handle Rabbit MQ Cluster.
return hostnames.Length == 1 ? connection.CreateConnection() : connection.CreateConnection(hostnames);
}
);
}

10
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/ModuleLoader_Tests.cs

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Logging;
using Volo.Abp.Modularity.PlugIns;
using Xunit;
@ -11,7 +12,12 @@ namespace Volo.Abp.Modularity
public void Should_Load_Modules_By_Dependency_Order()
{
var moduleLoader = new ModuleLoader();
var modules = moduleLoader.LoadModules(new ServiceCollection(), typeof(MyStartupModule), new PlugInSourceList());
var modules = moduleLoader.LoadModules(
new ServiceCollection()
.AddSingleton<IInitLoggerFactory>(new DefaultInitLoggerFactory()),
typeof(MyStartupModule),
new PlugInSourceList()
);
modules.Length.ShouldBe(2);
modules[0].Type.ShouldBe(typeof(IndependentEmptyModule));
modules[1].Type.ShouldBe(typeof(MyStartupModule));
@ -22,7 +28,7 @@ namespace Volo.Abp.Modularity
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
}

128
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs

@ -4,6 +4,8 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Caching;
using Volo.Blogging.Comments;
using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos;
@ -18,71 +20,41 @@ namespace Volo.Blogging.Posts
private readonly IPostRepository _postRepository;
private readonly ITagRepository _tagRepository;
private readonly ICommentRepository _commentRepository;
private readonly IDistributedCache<ListResultDto<PostWithDetailsDto>> _postsCache;
public PostAppService(IPostRepository postRepository, ITagRepository tagRepository, ICommentRepository commentRepository, IBlogUserLookupService userLookupService)
public PostAppService(IPostRepository postRepository, ITagRepository tagRepository, ICommentRepository commentRepository, IBlogUserLookupService userLookupService, IDistributedCache<ListResultDto<PostWithDetailsDto>> postsCache)
{
UserLookupService = userLookupService;
_postRepository = postRepository;
_tagRepository = tagRepository;
_commentRepository = commentRepository;
_postsCache = postsCache;
}
public async Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid id, string tagName)
{
var posts = await _postRepository.GetPostsByBlogId(id);
var tag = tagName.IsNullOrWhiteSpace() ? null : await _tagRepository.FindByNameAsync(id, tagName);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
postDto.Tags = await GetTagsOfPost(postDto.Id);
}
if (tag != null)
{
postDtos = await FilterPostsByTag(postDtos, tag);
}
foreach (var postDto in postDtos)
{
if (postDto.CreatorId.HasValue)
var cacheKey = id.ToString() + "-" + tagName;
return await _postsCache.GetOrAddAsync(
cacheKey,
async () => await GetPostsByBlogIdAndTagName(id, tagName),
() => new DistributedCacheEntryOptions
{
if (!userDictionary.ContainsKey(postDto.CreatorId.Value))
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
{
userDictionary[creatorUser.Id] = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
}
if (userDictionary.ContainsKey(postDto.CreatorId.Value))
{
postDto.Writer = userDictionary[(Guid)postDto.CreatorId];
}
AbsoluteExpiration = DateTimeOffset.Now.AddHours(6)
}
}
return new ListResultDto<PostWithDetailsDto>(postDtos);
);
}
public async Task<ListResultDto<PostWithDetailsDto>> GetTimeOrderedListAsync(Guid blogId)
{
var posts = await _postRepository.GetOrderedList(blogId);
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
return await _postsCache.GetOrAddAsync(
blogId.ToString(),
async () => await GetTimeOrderedPostsAsync(blogId),
() => new DistributedCacheEntryOptions
{
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
AbsoluteExpiration = DateTimeOffset.Now.AddHours(6)
}
}
return new ListResultDto<PostWithDetailsDto>(postDtos);
);
}
public async Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input)
@ -185,6 +157,64 @@ namespace Volo.Blogging.Posts
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
private async Task<ListResultDto<PostWithDetailsDto>> GetPostsByBlogIdAndTagName(Guid id, string tagName)
{
var posts = await _postRepository.GetPostsByBlogId(id);
var tag = tagName.IsNullOrWhiteSpace() ? null : await _tagRepository.FindByNameAsync(id, tagName);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
postDto.Tags = await GetTagsOfPost(postDto.Id);
}
if (tag != null)
{
postDtos = await FilterPostsByTag(postDtos, tag);
}
foreach (var postDto in postDtos)
{
if (postDto.CreatorId.HasValue)
{
if (!userDictionary.ContainsKey(postDto.CreatorId.Value))
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
{
userDictionary[creatorUser.Id] = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
}
if (userDictionary.ContainsKey(postDto.CreatorId.Value))
{
postDto.Writer = userDictionary[(Guid)postDto.CreatorId];
}
}
}
return new ListResultDto<PostWithDetailsDto>(postDtos);
}
private async Task<ListResultDto<PostWithDetailsDto>> GetTimeOrderedPostsAsync(Guid blogId)
{
var posts = await _postRepository.GetOrderedList(blogId);
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
{
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
}
return new ListResultDto<PostWithDetailsDto>(postDtos);
}
private async Task<string> RenameUrlIfItAlreadyExistAsync(Guid blogId, string url, Post existingPost = null)
{
if (await _postRepository.IsPostUrlInUseAsync(blogId, url, existingPost?.Id))

2012
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.Designer.cs

File diff suppressed because it is too large

25
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.cs

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Added_EntityType_to_Media : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "EntityType",
table: "CmsMediaDescriptors",
type: "nvarchar(64)",
maxLength: 64,
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EntityType",
table: "CmsMediaDescriptors");
}
}
}

2016
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.Designer.cs

File diff suppressed because it is too large

24
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Added_Content_To_BlogPost : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Content",
table: "CmsBlogPosts",
type: "nvarchar(max)",
maxLength: 2147483647,
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Content",
table: "CmsBlogPosts");
}
}
}

2020
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.Designer.cs

File diff suppressed because it is too large

24
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Added_Content_To_Page : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Content",
table: "CmsPages",
type: "nvarchar(max)",
maxLength: 2147483647,
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Content",
table: "CmsPages");
}
}
}

1951
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210305122554_BlogPost_CoverImage.Designer.cs

File diff suppressed because it is too large

56
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210305122554_BlogPost_CoverImage.cs

@ -0,0 +1,56 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class BlogPost_CoverImage : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CmsContents");
migrationBuilder.AddColumn<Guid>(
name: "CoverImageMediaId",
table: "CmsBlogPosts",
type: "uniqueidentifier",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CoverImageMediaId",
table: "CmsBlogPosts");
migrationBuilder.CreateTable(
name: "CmsContents",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Value = table.Column<string>(type: "nvarchar(max)", maxLength: 2147483647, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsContents", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_CmsContents_TenantId_EntityType_EntityId",
table: "CmsContents",
columns: new[] { "TenantId", "EntityType", "EntityId" });
}
}
}

80
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

@ -1269,6 +1269,13 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("CoverImageMediaId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -1375,7 +1382,7 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsComments");
});
modelBuilder.Entity("Volo.CmsKit.Contents.Content", b =>
modelBuilder.Entity("Volo.CmsKit.MediaDescriptors.MediaDescriptor", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
@ -1403,11 +1410,6 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
@ -1431,68 +1433,6 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId");
b.ToTable("CmsContents");
});
modelBuilder.Entity("Volo.CmsKit.MediaDescriptors.MediaDescriptor", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("MimeType")
.IsRequired()
.HasMaxLength(128)
@ -1528,6 +1468,10 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");

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

@ -8,7 +8,6 @@
@using Volo.CmsKit.GlobalFeatures
@using Volo.CmsKit.Pages
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags
@ -31,11 +30,6 @@
<abp-card>
<abp-card-body class="p-5">
<abp-blockquote class="text-center">
@if (GlobalFeatureManager.Instance.IsEnabled<ContentsFeature>())
{
@await Component.InvokeAsync(typeof(ContentViewComponent), new {entityType = "quote", entityId = "1"})
}
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
@await Component.InvokeAsync(typeof(TagViewComponent), new

4
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj

@ -62,8 +62,4 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Migrations" />
</ItemGroup>
</Project>

7
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/tui-editor/tui-editor-jquery-patch.js

@ -0,0 +1,7 @@
/*
https://jquery.com/upgrade-guide/3.5/#jquery-htmlprefilter-changes
*/
var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi;
jQuery.htmlPrefilter = function (html) {
return html.replace(rxhtmlTag, "<$1></$2>");
};

7
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs

@ -11,6 +11,11 @@ namespace Volo.CmsKit.Admin.Blogs
public string Title { get; set; }
public string Slug { get; set; }
public string ShortDescription { get; protected set; }
public string ShortDescription { get; set; }
public string Content { get; set; }
public Guid? CoverImageMediaId { get; set; }
}
}

5
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs

@ -21,5 +21,10 @@ namespace Volo.CmsKit.Admin.Blogs
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))]
public string ShortDescription { get; set; }
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxContentLength))]
public string Content { get; set; }
public Guid? CoverImageMediaId { get; set; }
}
}

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs

@ -14,8 +14,5 @@ namespace Volo.CmsKit.Admin.Blogs
CreateBlogPostDto,
UpdateBlogPostDto>
{
Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent);
Task<RemoteStreamContent> GetCoverImageAsync(Guid id);
}
}

4
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs

@ -18,6 +18,10 @@ namespace Volo.CmsKit.Admin.Blogs
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))]
public string ShortDescription { get; set; }
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxContentLength))]
public string Content { get; set; }
public Guid? CoverImageMediaId { get; set; }
}
}

23
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentCreateDto.cs

@ -1,23 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Admin.Contents
{
[Serializable]
public class ContentCreateDto
{
[Required]
[DynamicMaxLength(typeof(ContentConsts), nameof(ContentConsts.MaxEntityTypeLength))]
public string EntityType { get; set; }
[Required]
[DynamicMaxLength(typeof(ContentConsts), nameof(ContentConsts.MaxEntityIdLength))]
public string EntityId { get; set; }
[Required]
[DynamicMaxLength(typeof(ContentConsts), nameof(ContentConsts.MaxValueLength))]
public string Value { get; set; }
}
}

15
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentDto.cs

@ -1,15 +0,0 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Admin.Contents
{
[Serializable]
public class ContentDto : EntityDto<Guid>
{
public string EntityType { get; set; }
public string EntityId { get; set; }
public string Value { get; set; }
}
}

13
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs

@ -1,13 +0,0 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Admin.Contents
{
[Serializable]
public class ContentGetListDto : EntityDto<Guid>
{
public string EntityType { get; set; }
public string EntityId { get; set; }
}
}

10
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListInput.cs

@ -1,10 +0,0 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Admin.Contents
{
[Serializable]
public class ContentGetListInput : PagedAndSortedResultRequestDto
{
}
}

15
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentUpdateDto.cs

@ -1,15 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Admin.Contents
{
[Serializable]
public class ContentUpdateDto
{
[Required]
[DynamicMaxLength(typeof(ContentConsts), nameof(ContentConsts.MaxValueLength))]
public string Value { get; set; }
}
}

19
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs

@ -1,19 +0,0 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.CmsKit.Admin.Contents;
namespace Volo.CmsKit.Admin.Contents
{
public interface IContentAdminAppService
: ICrudAppService<
ContentDto,
ContentGetListDto,
Guid,
ContentGetListInput,
ContentCreateDto,
ContentUpdateDto>
{
Task<ContentDto> GetAsync(string entityType, string entityId);
}
}

4
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/CreateMediaInputStream.cs

@ -8,6 +8,10 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{
public class CreateMediaInputStream : RemoteStreamContent
{
[Required]
[DynamicStringLength(typeof(MediaDescriptorConsts), nameof(MediaDescriptorConsts.MaxEntityTypeLength))]
public string EntityType { get; set; }
[Required]
[DynamicStringLength(typeof(MediaDescriptorConsts), nameof(MediaDescriptorConsts.MaxNameLength))]
public string Name { get; set; }

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs

@ -16,5 +16,8 @@ namespace Volo.CmsKit.Admin.Pages
[Required]
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxSlugLength))]
public string Slug { get; set; }
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxContentLength))]
public string Content { get; set; }
}
}

2
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs

@ -9,5 +9,7 @@ namespace Volo.CmsKit.Admin.Pages
public string Title { get; set; }
public string Slug { get; set; }
public string Content { get; set; }
}
}

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs

@ -16,5 +16,8 @@ namespace Volo.CmsKit.Admin.Pages
[Required]
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxSlugLength))]
public string Slug { get; set; }
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxContentLength))]
public string Content { get; set; }
}
}

13
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs

@ -52,16 +52,9 @@ namespace Volo.CmsKit.Permissions
blogManagement.AddChild(CmsKitAdminPermissions.Blogs.Features, L("Permission:BlogManagement.Features"));
var blogPostManagement = cmsGroup.AddPermission(CmsKitAdminPermissions.BlogPosts.Default, L("Permission:BlogPostManagement"));
blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Create, L("Permission:BlogPostManagement.Create"));
blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Update, L("Permission:BlogPostManagement.Update"));
blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Delete, L("Permission:BlogPostManagement.Delete"));
}
if (GlobalFeatureManager.Instance.IsEnabled<MediaFeature>())
{
var mediaDescriptorManagement = cmsGroup.AddPermission(CmsKitAdminPermissions.MediaDescriptors.Default, L("Permission:MediaDescriptorManagement"));
mediaDescriptorManagement.AddChild(CmsKitAdminPermissions.MediaDescriptors.Create, L("Permission:MediaDescriptorManagement:Create"));
mediaDescriptorManagement.AddChild(CmsKitAdminPermissions.MediaDescriptors.Delete, L("Permission:MediaDescriptorManagement:Delete"));
blogPostManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Create, L("Permission:BlogPostManagement.Create"));
blogPostManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Update, L("Permission:BlogPostManagement.Update"));
blogPostManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Delete, L("Permission:BlogPostManagement.Delete"));
}
}

13
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs

@ -5,13 +5,13 @@ namespace Volo.CmsKit.Permissions
public class CmsKitAdminPermissions
{
public const string GroupName = "CmsKit";
public static class Comments
{
public const string Default = GroupName + ".Comments";
public const string Delete = Default + ".Delete";
}
public static class Tags
{
public const string Default = GroupName + ".Tags";
@ -19,7 +19,7 @@ namespace Volo.CmsKit.Permissions
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class Contents
{
public const string Default = GroupName + ".Contents";
@ -52,12 +52,5 @@ namespace Volo.CmsKit.Permissions
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class MediaDescriptors
{
public const string Default = GroupName + ".MediaDescriptors";
public const string Create = Default + ".Create";
public const string Delete = Default + ".Delete";
}
}
}

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

@ -18,27 +18,23 @@ namespace Volo.CmsKit.Admin.Blogs
{
[RequiresGlobalFeature(typeof(BlogsFeature))]
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public class BlogPostAdminAppService: CmsKitAppServiceBase, IBlogPostAdminAppService
public class BlogPostAdminAppService : CmsKitAppServiceBase, IBlogPostAdminAppService
{
protected BlogPostManager BlogPostManager { get; }
protected IBlogPostRepository BlogPostRepository { get; }
protected IBlogRepository BlogRepository { get; }
protected IBlobContainer<BlogPostCoverImageContainer> BlobContainer { get; }
protected ICmsUserLookupService UserLookupService { get; }
public BlogPostAdminAppService(
BlogPostManager blogPostManager,
IBlogPostRepository blogPostRepository,
IBlogRepository blogRepository,
IBlobContainer<BlogPostCoverImageContainer> blobContainer,
ICmsUserLookupService userLookupService)
{
BlogPostManager = blogPostManager;
BlogPostRepository = blogPostRepository;
BlogRepository = blogRepository;
BlobContainer = blobContainer;
UserLookupService = userLookupService;
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Create)]
@ -54,7 +50,8 @@ namespace Volo.CmsKit.Admin.Blogs
input.Title,
input.Slug,
input.ShortDescription,
CurrentTenant.Id);
input.Content,
input.CoverImageMediaId);
await BlogPostRepository.InsertAsync(blogPost);
@ -68,6 +65,8 @@ namespace Volo.CmsKit.Admin.Blogs
blogPost.SetTitle(input.Title);
blogPost.SetShortDescription(input.ShortDescription);
blogPost.SetContent(input.Content);
blogPost.CoverImageMediaId = input.CoverImageMediaId;
if (blogPost.Slug != input.Slug)
{
@ -76,26 +75,7 @@ namespace Volo.CmsKit.Admin.Blogs
await BlogPostRepository.UpdateAsync(blogPost);
return ObjectMapper.Map<BlogPost,BlogPostDto>(blogPost);
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Update)]
public virtual async Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent)
{
await BlogPostRepository.GetAsync(id);
using (var stream = streamContent.GetStream())
{
await BlobContainer.SaveAsync(id.ToString(), stream, overrideExisting: true);
}
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public virtual async Task<RemoteStreamContent> GetCoverImageAsync(Guid id)
{
var stream = await BlobContainer.GetAsync(id.ToString());
return new RemoteStreamContent(stream);
return ObjectMapper.Map<BlogPost, BlogPostDto>(blogPost);
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]

31
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs

@ -1,4 +1,13 @@
namespace Volo.CmsKit.Admin
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Authorization;
namespace Volo.CmsKit.Admin
{
public abstract class CmsKitAdminAppServiceBase : CmsKitAppServiceBase
{
@ -6,5 +15,25 @@
{
ObjectMapperContext = typeof(CmsKitAdminApplicationModule);
}
/// <summary>
/// Checks given policies until finding granted policy. If none of them is granted, throws <see cref="AbpAuthorizationException"/>
/// </summary>
/// <param name="policies">Policies to be checked.</param>
/// <exception cref="AbpAuthorizationException">Thrown when none of policies is granted.</exception>
protected async Task CheckAnyOfPoliciesAsync([NotNull] IEnumerable<string> policies)
{
Check.NotNull(policies, nameof(policies));
foreach (var policy in policies)
{
if (await AuthorizationService.IsGrantedAsync(policy))
{
return;
}
}
throw new AbpAuthorizationException();
}
}
}

7
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs

@ -2,13 +2,11 @@
using Volo.Abp.AutoMapper;
using Volo.CmsKit.Admin.Blogs;
using Volo.CmsKit.Admin.Comments;
using Volo.CmsKit.Admin.Contents;
using Volo.CmsKit.Admin.MediaDescriptors;
using Volo.CmsKit.Admin.Pages;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Admin.Tags;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Tags;
@ -28,11 +26,6 @@ namespace Volo.CmsKit.Admin
CreateMap<Page, PageDto>();
CreateMap<Content, ContentDto>(MemberList.Destination);
CreateMap<Content, ContentGetListDto>(MemberList.Destination);
CreateMap<ContentCreateDto, Content>(MemberList.Source);
CreateMap<ContentUpdateDto, Content>(MemberList.Source);
CreateMap<BlogPost, BlogPostDto>(MemberList.Destination);
CreateMap<CreateBlogPostDto, BlogPost>(MemberList.Source);
CreateMap<UpdateBlogPostDto, BlogPost>(MemberList.Source);

88
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs

@ -5,8 +5,11 @@ using Volo.Abp.GlobalFeatures;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Localization;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Permissions;
using Volo.CmsKit.Tags;
@ -19,15 +22,14 @@ namespace Volo.CmsKit.Admin
)]
public class CmsKitAdminApplicationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
ConfigureTagOptions();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<CmsKitAdminApplicationModule>();
ConfigureTagOptions();
ConfigureCommentOptions();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<CmsKitAdminApplicationModule>(validate: true);
@ -44,11 +46,81 @@ namespace Volo.CmsKit.Admin
new TagEntityTypeDefiniton(
BlogPostConsts.EntityType,
LocalizableString.Create<CmsKitResource>("BlogPost"),
CmsKitAdminPermissions.BlogPosts.Update,
CmsKitAdminPermissions.BlogPosts.Update,
CmsKitAdminPermissions.BlogPosts.Update));
createPolicies: new[]
{
CmsKitAdminPermissions.BlogPosts.Create,
CmsKitAdminPermissions.BlogPosts.Update
},
updatePolicies: new[]
{
CmsKitAdminPermissions.BlogPosts.Create,
CmsKitAdminPermissions.BlogPosts.Update
},
deletePolicies: new[]
{
CmsKitAdminPermissions.BlogPosts.Create,
CmsKitAdminPermissions.BlogPosts.Update
}));
}
});
if (GlobalFeatureManager.Instance.IsEnabled<MediaFeature>())
{
Configure<CmsKitMediaOptions>(options =>
{
if (GlobalFeatureManager.Instance.IsEnabled<BlogsFeature>())
{
options.EntityTypes.AddIfNotContains(
new MediaDescriptorDefinition(
BlogPostConsts.EntityType,
createPolicies: new[]
{
CmsKitAdminPermissions.BlogPosts.Create,
CmsKitAdminPermissions.BlogPosts.Update
},
deletePolicies: new[]
{
CmsKitAdminPermissions.BlogPosts.Create,
CmsKitAdminPermissions.BlogPosts.Update,
CmsKitAdminPermissions.BlogPosts.Delete
}));
}
if (GlobalFeatureManager.Instance.IsEnabled<PagesFeature>())
{
options.EntityTypes.AddIfNotContains(
new MediaDescriptorDefinition(
PageConsts.EntityType,
createPolicies: new[]
{
CmsKitAdminPermissions.Pages.Create,
CmsKitAdminPermissions.Pages.Update
},
deletePolicies: new[]
{
CmsKitAdminPermissions.Pages.Create,
CmsKitAdminPermissions.Pages.Update,
CmsKitAdminPermissions.Pages.Delete
}));
}
});
}
}
private void ConfigureCommentOptions()
{
if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
Configure<CmsKitCommentOptions>(options =>
{
if (GlobalFeatureManager.Instance.IsEnabled<BlogsFeature>())
{
options.EntityTypes.AddIfNotContains(
new CommentEntityTypeDefinition(BlogPostConsts.EntityType));
}
});
}
}
}
}

71
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs

@ -1,71 +0,0 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Permissions;
namespace Volo.CmsKit.Admin.Contents
{
[Authorize(CmsKitAdminPermissions.Contents.Default)]
public class ContentAdminAppService :
CrudAppService<
Content,
ContentDto,
ContentGetListDto,
Guid,
ContentGetListInput,
ContentCreateDto,
ContentUpdateDto
>, IContentAdminAppService
{
protected IContentManager ContentManager { get; }
protected IContentRepository ContentRepository { get; }
public ContentAdminAppService(
IRepository<Content, Guid> repository,
IContentManager contentManager,
IContentRepository contentRepository) : base(repository)
{
ContentManager = contentManager;
ContentRepository = contentRepository;
GetListPolicyName = CmsKitAdminPermissions.Contents.Default;
GetPolicyName = CmsKitAdminPermissions.Contents.Default;
CreatePolicyName = CmsKitAdminPermissions.Contents.Create;
UpdatePolicyName = CmsKitAdminPermissions.Contents.Update;
DeletePolicyName = CmsKitAdminPermissions.Contents.Delete;
}
[Authorize(CmsKitAdminPermissions.Contents.Create)]
public override async Task<ContentDto> CreateAsync(ContentCreateDto input)
{
var entity = new Content(
GuidGenerator.Create(),
input.EntityType,
input.EntityId,
input.Value,
CurrentTenant?.Id);
await ContentManager.InsertAsync(entity);
return await MapToGetOutputDtoAsync(entity);
}
public virtual async Task<ContentDto> GetAsync(
[NotNull] string entityType,
[NotNull] string entityId)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var content = await ContentRepository.GetAsync(entityType, entityId, CurrentTenant?.Id);
return ObjectMapper.Map<Content, ContentDto>(content);
}
}
}

42
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs

@ -10,34 +10,50 @@ using Volo.CmsKit.Permissions;
namespace Volo.CmsKit.Admin.MediaDescriptors
{
[RequiresGlobalFeature(typeof(MediaFeature))]
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Default)]
public class MediaDescriptorAdminAppService : CmsKitAdminAppServiceBase, IMediaDescriptorAdminAppService
{
protected readonly IBlobContainer<MediaContainer> MediaContainer;
protected readonly IMediaDescriptorRepository MediaDescriptorRepository;
public MediaDescriptorAdminAppService(IBlobContainer<MediaContainer> mediaContainer, IMediaDescriptorRepository mediaDescriptorRepository)
protected IBlobContainer<MediaContainer> MediaContainer { get; }
protected IMediaDescriptorRepository MediaDescriptorRepository { get; }
protected MediaDescriptorManager MediaDescriptorManager { get; }
protected IMediaDescriptorDefinitionStore MediaDescriptorDefinitionStore { get; }
public MediaDescriptorAdminAppService(
IBlobContainer<MediaContainer> mediaContainer,
IMediaDescriptorRepository mediaDescriptorRepository,
MediaDescriptorManager mediaDescriptorManager,
IMediaDescriptorDefinitionStore mediaDescriptorDefinitionStore)
{
MediaContainer = mediaContainer;
MediaDescriptorRepository = mediaDescriptorRepository;
MediaDescriptorManager = mediaDescriptorManager;
MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore;
}
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)]
public virtual async Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream)
{
var newId = GuidGenerator.Create();
var stream = inputStream.GetStream();
var newEntity = new MediaDescriptor(newId, inputStream.Name, inputStream.ContentType, stream.Length, CurrentTenant.Id);
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType);
await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
using (var stream = inputStream.GetStream())
{
var newEntity = await MediaDescriptorManager.CreateAsync(inputStream.EntityType, inputStream.Name, inputStream.ContentType, inputStream.ContentLength ?? 0);
await MediaContainer.SaveAsync(newId.ToString(), stream);
await MediaDescriptorRepository.InsertAsync(newEntity);
await MediaContainer.SaveAsync(newEntity.Id.ToString(), stream);
await MediaDescriptorRepository.InsertAsync(newEntity);
return ObjectMapper.Map<MediaDescriptor, MediaDescriptorDto>(newEntity);
return ObjectMapper.Map<MediaDescriptor, MediaDescriptorDto>(newEntity);
}
}
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Delete)]
public virtual async Task DeleteAsync(Guid id)
{
var mediaDescriptor = await MediaDescriptorRepository.GetAsync(id);
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(mediaDescriptor.EntityType);
await CheckAnyOfPoliciesAsync(definition.DeletePolicies);
await MediaContainer.DeleteAsync(id.ToString());
await MediaDescriptorRepository.DeleteAsync(id);
}

3
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs

@ -50,7 +50,7 @@ namespace Volo.CmsKit.Admin.Pages
{
await CheckPageSlugAsync(input.Slug);
var page = new Page(GuidGenerator.Create(), input.Title, input.Slug, CurrentTenant.Id);
var page = new Page(GuidGenerator.Create(), input.Title, input.Slug, input.Content, CurrentTenant.Id);
await PageRepository.InsertAsync(page);
@ -69,6 +69,7 @@ namespace Volo.CmsKit.Admin.Pages
page.SetTitle(input.Title);
page.SetSlug(input.Slug);
page.SetContent(input.Content);
await PageRepository.UpdateAsync(page);

6
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs

@ -31,7 +31,7 @@ namespace Volo.CmsKit.Admin.Tags
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
await CheckPolicyAsync(definition.CreatePolicy);
await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
var tag = await TagManager.GetOrAddAsync(input.EntityType, input.TagName);
@ -46,7 +46,7 @@ namespace Volo.CmsKit.Admin.Tags
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
await CheckPolicyAsync(definition.DeletePolicy);
await CheckAnyOfPoliciesAsync(definition.DeletePolicies);
await EntityTagManager.RemoveTagFromEntityAsync(
input.TagId,
@ -59,7 +59,7 @@ namespace Volo.CmsKit.Admin.Tags
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
await CheckPolicyAsync(definition.UpdatePolicy);
await CheckAnyOfPoliciesAsync(definition.UpdatePolicies);
await EntityTagManager.SetEntityTagsAsync(input.EntityType, input.EntityId, input.Tags);
}

44
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs

@ -1,14 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Permissions;
@ -52,50 +47,13 @@ namespace Volo.CmsKit.Admin.Blogs
return BlogPostAdminAppService.GetAsync(id);
}
[HttpGet]
[Route("{id}/cover-image")]
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public virtual Task<RemoteStreamContent> GetCoverImageAsync(Guid id)
{
Response.Headers.Add("Content-Disposition", $"inline;filename=\"{id}\"");
Response.Headers.Add("Accept-Ranges", "bytes");
Response.Headers.Add("Cache-Control", "max-age=120");
Response.ContentType = "image";
return BlogPostAdminAppService.GetCoverImageAsync(id);
}
[HttpGet]
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public virtual Task<PagedResultDto<BlogPostDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
return BlogPostAdminAppService.GetListAsync(input);
}
[NonAction]
public virtual Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent)
{
return BlogPostAdminAppService.SetCoverImageAsync(id, streamContent);
}
[HttpPost]
[Route("{id}/cover-image")]
[Authorize(CmsKitAdminPermissions.BlogPosts.Update)]
public virtual async Task<IActionResult> UploadCoverImageAsync(Guid id, IFormFile file)
{
if (file == null)
{
return BadRequest();
}
using (var stream = file.OpenReadStream())
{
await BlogPostAdminAppService.SetCoverImageAsync(id, new RemoteStreamContent(stream));
}
return CreatedAtAction(nameof(GetCoverImageAsync), new { id });
}
[HttpPut]
[Route("{id}")]
[Authorize(CmsKitAdminPermissions.BlogPosts.Update)]

71
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs

@ -1,71 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Admin.Contents;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Permissions;
namespace Volo.CmsKit.Admin.Contents
{
[Authorize(CmsKitAdminPermissions.Contents.Default)]
[RequiresGlobalFeature(typeof(ContentsFeature))]
[RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit-admin/contents")]
public class ContentAdminController : CmsKitAdminController, IContentAdminAppService
{
protected IContentAdminAppService ContentAdminAppService { get; }
public ContentAdminController(IContentAdminAppService contentAdminAppService)
{
ContentAdminAppService = contentAdminAppService;
}
[HttpPost]
[Authorize(CmsKitAdminPermissions.Contents.Create)]
public Task<ContentDto> CreateAsync(ContentCreateDto input)
{
return ContentAdminAppService.CreateAsync(input);
}
[HttpDelete("{id}")]
[Authorize(CmsKitAdminPermissions.Contents.Delete)]
public Task DeleteAsync(Guid id)
{
return ContentAdminAppService.DeleteAsync(id);
}
[HttpGet("{id}")]
[Authorize(CmsKitAdminPermissions.Contents.Default)]
public Task<ContentDto> GetAsync(Guid id)
{
return ContentAdminAppService.GetAsync(id);
}
[HttpGet]
[Authorize(CmsKitAdminPermissions.Contents.Default)]
public Task<PagedResultDto<ContentGetListDto>> GetListAsync(ContentGetListInput input)
{
return ContentAdminAppService.GetListAsync(input);
}
[HttpPut]
[Authorize(CmsKitAdminPermissions.Contents.Update)]
public Task<ContentDto> UpdateAsync(Guid id, ContentUpdateDto input)
{
return ContentAdminAppService.UpdateAsync(id, input);
}
[HttpGet]
[Route("{entityType}/{entityId}")]
[Authorize(CmsKitAdminPermissions.Contents.Default)]
public Task<ContentDto> GetAsync(string entityType, string entityId)
{
return ContentAdminAppService.GetAsync(entityType, entityId);
}
}
}

8
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs

@ -13,7 +13,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{
[RequiresGlobalFeature(typeof(MediaFeature))]
[RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)]
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Default)]
[Area("cms-kit")]
[Route("api/cms-kit-admin/media")]
public class MediaDescriptorAdminController : CmsKitAdminController, IMediaDescriptorAdminAppService
@ -26,7 +25,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
}
[HttpPost]
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)]
[NonAction]
public virtual Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream)
{
@ -35,15 +33,14 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
[HttpDelete]
[Route("{id}")]
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Delete)]
public virtual Task DeleteAsync(Guid id)
{
return MediaDescriptorAdminAppService.DeleteAsync(id);
}
[HttpPost]
[Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)]
public virtual async Task<IActionResult> UploadAsync(IFormFile file)
[Route("{entityType}")]
public virtual async Task<IActionResult> UploadAsync(string entityType, IFormFile file)
{
if (file == null)
{
@ -52,6 +49,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
var inputStream = new CreateMediaInputStream(file.OpenReadStream())
{
EntityType = entityType,
ContentType = file.ContentType,
Name = file.FileName
};

6
modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs

@ -76,13 +76,13 @@ namespace Volo.CmsKit.Admin.Web
options.Conventions.AddPageRoute("/CmsKit/Tags/Index", "/Cms/Tags");
options.Conventions.AddPageRoute("/CmsKit/Pages/Index", "/Cms/Pages");
options.Conventions.AddPageRoute("/CmsKit/Pages/Create", "/Cms/Pages/Create");
options.Conventions.AddPageRoute("/CmsKit/Pages/Update", "/Cms/Pages/Update");
options.Conventions.AddPageRoute("/CmsKit/Pages/Update", "/Cms/Pages/Update/{Id}");
options.Conventions.AddPageRoute("/CmsKit/Blogs/Index", "/Cms/Blogs");
options.Conventions.AddPageRoute("/CmsKit/BlogPosts/Index", "/Cms/BlogPosts");
options.Conventions.AddPageRoute("/CmsKit/BlogPosts/Create", "/Cms/BlogPosts/Create");
options.Conventions.AddPageRoute("/CmsKit/BlogPosts/Update", "/Cms/BlogPosts/Update");
options.Conventions.AddPageRoute("/CmsKit/BlogPosts/Update", "/Cms/BlogPosts/Update/{Id}");
options.Conventions.AddPageRoute("/CmsKit/Comments/Index", "/Cms/Comments");
options.Conventions.AddPageRoute("/CmsKit/Comments/Details", "/Cms/Details");
options.Conventions.AddPageRoute("/CmsKit/Comments/Details", "/Cms/Comments/{Id}");
});
Configure<AbpPageToolbarOptions>(options =>

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

@ -1,6 +1,7 @@
@page
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor
@using System.Globalization
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar
@using Volo.CmsKit.Admin.Web.Pages
@using Volo.CmsKit.Admin.Web.Menus
@ -22,12 +23,19 @@
@section scripts {
<abp-script-bundle>
<abp-script type="typeof(UppyScriptContributor)" />
<abp-script src="/Pages/CmsKit/BlogPosts/create.js" />
<abp-script type="typeof(SlugifyScriptContributor)" />
<abp-script type="typeof(TuiEditorScriptContributor)"/>
<abp-script type="typeof(UppyScriptContributor)"/>
<abp-script type="typeof(SlugifyScriptContributor)"/>
<abp-script src="/Pages/CmsKit/BlogPosts/create.js"/>
</abp-script-bundle>
}
@section styles {
<abp-style-bundle>
<abp-style type="typeof(TuiEditorStyleContributor)"/>
</abp-style-bundle>
}
@section content_toolbar {
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(CreateModel).FullName })
}
@ -50,15 +58,14 @@
<abp-input asp-for="ViewModel.Title" />
<abp-input asp-for="ViewModel.Slug" />
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new
{
entityType = BlogPostConsts.EntityType,
displaySubmitButton = false
})
<abp-form-content />
<div class="content-editor"
id="ContentEditor"
data-input-id="@Html.IdFor(x => x.ViewModel.Content)"
data-language="@(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)">
</div>
</abp-dynamic-form>
<div id="blog-post-tags-wrapper">

7
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Create.cshtml.cs

@ -58,6 +58,13 @@ namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))]
public string ShortDescription { get; set; }
[HiddenInput]
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxContentLength))]
public string Content { get; set; }
[HiddenInput]
public Guid? CoverImageMediaId { get; set; }
}
}
}

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

@ -1,9 +1,10 @@
@page "{Id}"
@using System.Globalization
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor
@using Volo.CmsKit.Blogs
@using Volo.CmsKit.Admin.Web.Pages
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor
@using Volo.CmsKit.Admin.Web.Menus
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags.Components.TagEditor
@ -22,12 +23,19 @@
@section scripts {
<abp-script-bundle>
<abp-script type="typeof(UppyScriptContributor)" />
<abp-script src="/Pages/CmsKit/BlogPosts/update.js" />
<abp-script type="typeof(SlugifyScriptContributor)" />
<abp-script type="typeof(TuiEditorScriptContributor)"/>
<abp-script type="typeof(UppyScriptContributor)"/>
<abp-script type="typeof(SlugifyScriptContributor)"/>
<abp-script src="/Pages/CmsKit/BlogPosts/update.js"/>
</abp-script-bundle>
}
@section styles {
<abp-style-bundle>
<abp-style type="typeof(TuiEditorStyleContributor)"/>
</abp-style-bundle>
}
@section content_toolbar {
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(UpdateModel).FullName })
}
@ -37,7 +45,7 @@
<abp-card-body>
<div class="form-group">
<img height="120" src="/api/cms-kit-admin/blogs/blog-posts/@Model.Id/cover-image" />
<img height="120" src="/api/cms-kit/media/@Model.ViewModel.CoverImageMediaId" />
<label>@L["CoverImage"]</label>
<input type="file" id="BlogPostCoverImage" class="form-control" />
</div>
@ -49,14 +57,14 @@
<abp-input asp-for="ViewModel.Slug" />
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new
{
entityType = BlogPostConsts.EntityType,
entityId = Model.Id.ToString(),
displaySubmitButton = false
})
<abp-form-content />
<div class="content-editor"
id="ContentEditor"
data-input-id="@Html.IdFor(x => x.ViewModel.Content)"
data-language="@(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)">
</div>
</abp-dynamic-form>
@if (Model.TagsFeature?.IsEnabled == true)

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

@ -72,6 +72,13 @@ namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))]
[DisplayOrder(10001)]
public string ShortDescription { get; set; }
[HiddenInput]
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxContentLength))]
public string Content { get; set; }
[HiddenInput]
public Guid? CoverImageMediaId { get; set; }
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save