Browse Source

Merge branch 'dev' into adding-widget-to-editor

pull/13242/head
malik masis 4 years ago
parent
commit
4a6454d2c3
  1. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json
  2. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  3. 124
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  4. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/zh-Hans.json
  5. 8
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  6. 57
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  7. 101
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  8. 1
      docs/en/CLI.md
  9. 16
      docs/en/Multi-Tenancy.md
  10. 0
      docs/en/Themes/LeptonXLite/AspNetCore.md
  11. 2
      docs/en/Themes/LeptonXLite/angular.md
  12. 5
      docs/en/Themes/LeptonXLite/blazor.md
  13. 2
      docs/en/UI/Blazor/Theming.md
  14. 6
      docs/en/docs-nav.json
  15. 16
      docs/zh-Hans/Multi-Tenancy.md
  16. 15
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs
  17. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  18. 106
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListTemplatesCommand.cs
  19. 69
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs
  20. 14
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Templates/TemplateInfo.cs
  21. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SolutionName.cs
  22. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs
  23. 29
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  24. 94
      framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs
  25. 10
      framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs
  26. 23
      framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js
  27. 26
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Microsoft/AspNetCore/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs
  28. 2
      npm/ng-packs/packages/core/src/lib/core.module.ts
  29. 8
      npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts
  30. 50
      npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts
  31. 6
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
  32. 2
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html
  33. 4
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html
  34. 8
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html
  35. 4
      npm/ng-packs/packages/theme-shared/src/lib/constants/validation.ts
  36. 1
      npm/ng-packs/packages/theme-shared/src/lib/models/index.ts
  37. 1
      npm/ng-packs/packages/theme-shared/src/lib/models/validation.ts
  38. 10
      npm/ng-packs/packages/theme-shared/src/lib/tests/validation-utils.spec.ts
  39. 44
      npm/ng-packs/packages/theme-shared/src/lib/utils/validation-utils.ts
  40. 3
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs
  41. 3
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs
  42. 3
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs
  43. 3
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs
  44. 5
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs
  45. 5
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs
  46. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs
  47. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs
  48. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs
  49. 1
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs

4
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json

@ -11,6 +11,6 @@
"CommercialSupportWebSite": "商业版支持网站", "CommercialSupportWebSite": "商业版支持网站",
"CommunityWebSite": "ABP社区网站", "CommunityWebSite": "ABP社区网站",
"ManageAccount": "我的帐户 | ABP.IO", "ManageAccount": "我的帐户 | ABP.IO",
"ManageYourAccount": "管理您的帐户" "ManageYourProfile": "管理您的个人资料"
} }
} }

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

@ -161,6 +161,9 @@
"Error_Page_500_Title": "Looks like something went wrong!", "Error_Page_500_Title": "Looks like something went wrong!",
"Error_Page_500_Description_1": "We track these errors automatically, but if the problem persists feel free to <br /> contact us. In the meantime, try refreshing.", "Error_Page_500_Description_1": "We track these errors automatically, but if the problem persists feel free to <br /> contact us. In the meantime, try refreshing.",
"Error_Page_500_Description_2": "Contact with us at <a href=\"mailto:info@abp.io\" target=\"_blank\">info@abp.io</a>.", "Error_Page_500_Description_2": "Contact with us at <a href=\"mailto:info@abp.io\" target=\"_blank\">info@abp.io</a>.",
"Books": "Books" "Books": "Books",
"ABPDiscordServer": "ABP Discord Server",
"ABPCommunityTalks": "ABP Community Talks",
"ABPCommunityPosts": "ABP Community Posts"
} }
} }

124
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json

@ -14,6 +14,20 @@
"Volo.AbpIo.Domain:020002": "不能删除该NPM包因为\"{Modules}\"模块正在使用此包.", "Volo.AbpIo.Domain:020002": "不能删除该NPM包因为\"{Modules}\"模块正在使用此包.",
"Volo.AbpIo.Domain:020003": "不能删除该NPM包因为\"{Modules}\"模块正在使用此包并且\"{NugetPackages}\"Nuget包依赖此包.", "Volo.AbpIo.Domain:020003": "不能删除该NPM包因为\"{Modules}\"模块正在使用此包并且\"{NugetPackages}\"Nuget包依赖此包.",
"Volo.AbpIo.Domain:020004": "不能删除该Nuget包因为\"{Modules}\"模块正在使用此包.", "Volo.AbpIo.Domain:020004": "不能删除该Nuget包因为\"{Modules}\"模块正在使用此包.",
"Volo.AbpIo.Domain:030000": "您已经完成了试用期。",
"Volo.AbpIo.Domain:030001": "此组织名称已存在。",
"Volo.AbpIo.Domain:030002": "一旦激活,您将无法将试用许可证切换到 -requested- 状态!",
"Volo.AbpIo.Domain:030003": "没有这种状态!",
"Volo.AbpIo.Domain:030004": "由于意外错误,无法更改状态!",
"Volo.AbpIo.Domain:030005": "试用许可证处于激活状态时可以更新开始和结束日期!",
"Volo.AbpIo.Domain:030006": "结束日期必须大于开始日期!",
"Volo.AbpIo.Domain:030007": "此试用许可证已激活!",
"Volo.AbpIo.Domain:030008": "只有状态为-purchased-时才能设置购买日期!",
"Volo.AbpIo.Domain:030009": "找不到用户!",
"Volo.AbpIo.Domain:030010": "要购买试用许可证,您首先需要激活您的试用许可证!",
"Volo.AbpIo.Domain:030011": "购买后不能删除试用许可证!",
"Volo.AbpIo.Domain:070000": "组织名称只能包含拉丁字母、数字、点和连字符!",
"Volo.AbpIo.Domain:070001": "公司名称只能包含拉丁字母、数字、点、空格和连字符!",
"WantToLearn?": "想学习吗?", "WantToLearn?": "想学习吗?",
"ReadyToGetStarted?": "准备开始了吗?", "ReadyToGetStarted?": "准备开始了吗?",
"JoinOurCommunity": "加入我们的社区", "JoinOurCommunity": "加入我们的社区",
@ -39,6 +53,114 @@
"TrialLicensePeriodHasExpired": "您的试用许可期限已于 {0} 天前到期。", "TrialLicensePeriodHasExpired": "您的试用许可期限已于 {0} 天前到期。",
"TrialLicensePeriodWillExpire": "您的试用许可期限将在 {0} 天后到期。", "TrialLicensePeriodWillExpire": "您的试用许可期限将在 {0} 天后到期。",
"TrialLicensePeriodExpireToday": "您的试用许可期将于今天到期。", "TrialLicensePeriodExpireToday": "您的试用许可期将于今天到期。",
"PurchaseNow": "现在买!" "PurchaseNow": "现在买!",
"LatestReleaseLogs": "最新发布日志",
"RoadMap": "路线图",
"FAQ": "常见问题",
"SourceCode": "源代码",
"SeeAllPosts": "查看所有帖子",
"Contribute": "贡献",
"LiveDemo": "在线演示",
"GetLicense": "获得许可证",
"OpenSource": "开源",
"WebApplication": "Web应用程序",
"MeetTheABP": "认识 ABP",
"CompleteWebDevelopment": "一个完整的 Web 开发",
"Platform": "平台",
"ABPDescription": "ABP 框架是一个完整的基础架构,可通过遵循软件开发最佳实践和约定来创建现代 Web 应用程序。",
"StrongInfrastructure": "强大的基础设施",
"CompleteArchitecture": "完整的架构",
"DeveloperFocused": "以开发者为中心",
"ShareYourExperiences": "分享您使用 ABP 框架的经验",
"LatestPosts": "最新的帖子",
"Views": "意见",
"LearnLatestNewsAboutABPFramework": "获取有关 ABP 的最新相关信息,例如新版本、免费资源、帖子等。",
"DeveloperTools": "开发者工具",
"StartupTemplates": "启动模板",
"ApplicationModules": "应用模块",
"UI": "UI",
"Themes": "主题",
"Premium": "高级的",
"PrivacyPolicy": "隐私政策",
"TermsAndConditions": "条款 & 条件",
"WouldLikeToReceiveMarketingMaterials": "我想收到产品交易和特别优惠等市场推广材料。",
"JoinOurMarketingNewsletter": "加入我们市场推广时事通讯",
"CommunityPrivacyPolicyConfirmation": "我同意条款 & 条件和<a class=\"text-white fw-6 text-decoration-underline opacity-50\" href=\"https://commercial.abp.io/Privacy\">隐私政策 </a>。",
"WouldLikeToReceiveNotification": "我想从 abp.io 网站接收最新消息。",
"CommercialNewsletterConfirmationMessage": "我同意<a class=\"text-white fw-6 text-decoration-underline opacity-50\" href=\"https://commercial.abp.io/TermsConditions\">条款 & 条件</a > 和 <a class=\"text-white fw-6 text-decoration-underline opacity-50\" href=\"https://commercial.abp.io/Privacy\">隐私政策</a>。",
"FreeDDDEBook": "免费 DDD 电子书",
"AdditionalServices": "额外服务",
"Learn": "学习",
"AccountOverview": "账户信息",
"MyOrganizations": "我的组织",
"MySupportQuestions": "我的支持问题",
"MyProfile": "我的简介",
"Logout": "登出",
"Home": "主页",
"Posts": "帖子",
"JoinTheABPCommunity": "加入 ABP 社区",
"SubmitYourPost": "提交您的帖子",
"Modules": "模块",
"Tools": "工具",
"Pricing": "价格",
"ChangeLogs": "更改日志",
"SubscribeToNewsletter": "订阅时事通讯",
"SubscribeToNewsletterDescription": "获取有关 ABP 的最新相关信息,例如新版本、免费资源、帖子等。",
"EmailAddress": "邮箱地址",
"Subscribe": "订阅",
"WelcomeToABP": "欢迎来到 ABP",
"EULA": "最终用户许可协议",
"ABPCommercialIntroductionMessage": "预建应用程序模块、高级启动模板、快速应用程序开发工具、专业 UI 主题和高级支持。",
"MasteringAbpFrameworkEBook": "掌握 ABP 框架",
"MasteringTheABPFrameworkExplanation": "本书由 ABP 框架的创建者撰写,将帮助您全面了解框架和现代 Web 应用程序开发技术。",
"Speakers": "发言者",
"PreviousEvents": "以往活动",
"WatchTheEvent": "观看活动",
"RegisterNow": "现在注册",
"ThereIsNoEvent": "没有活动。",
"Events": "活动",
"Volo.AbpIo.Domain:080000": "已有一个名为 \"{Name}\" 的购买项目",
"MasteringAbpFrameworkBook": "书籍:掌握 ABP 框架",
"ABPIO-CommonPreferenceDefinition": "获取有关 ABP 平台的最新消息,例如新帖子、活动等。",
"BuiltOn": "建立在",
"AbpFramework": "ABP 框架",
"Volo.AbpIo.Domain:080001": "开始时间不能大于结束时间",
"Enum:BookType:0": "掌握 ABP 框架",
"Enum:PurchasePlatform:0": "亚马逊",
"Enum:PurchasePlatform:1": "Packt",
"Copied": "已复制!",
"CouldNotCopy": "无法复制!",
"CopyNotSupportByYourBrowser": "此功能在您使用的浏览器中不起作用。",
"City": "城市",
"ZipCode": "邮政编码",
"Address": "地址",
"Homepage": "主页",
"Year": "年份",
"Copyright": "版权所有 © <a href=\"{0}\" target=\"_blank\">{1}</a>",
"DomainDrivenDesign": "领域驱动设计",
"CrossCuttingConcerns": "横切关注点",
"AbpCommunity": "ABP 社区",
"Footer_GithubStarCount": "{0} 在 GitHub 上加星",
"Footer_NugetDownloadCount": "{0} 在 NuGet 上下载",
"AbpDescription": "ABP 是一个开源应用程序框架,专注于基于 AspNet Core 的 Web 应用程序开发。 Don't repeat yourself,专注于自己的业务代码。",
"Layout_AbpFramework_MetaTitle": "ABP 框架 - 开源 Web 应用程序框架",
"CommunityTalks_CountdownDays": "天",
"CommunityTalks_CountdownHours": "小时",
"CommunityTalks_CountdownMinutes": "分钟",
"CommunityTalks_CountdownSeconds": "秒",
"SeePreviousEvents": "查看以前的活动",
"CookieConsent_Accept": "接受",
"CookieConsent_Explanation_1": "我们使用 cookie 为您提供在我们网站上的最佳体验。",
"CookieConsent_Explanation_2": "如果您继续浏览,则表示您同意我们的<a href=\"@Url.Page(\"/Privacy\")\">隐私政策和cookie 政策。</a>。",
"Error_Page_400_Title": "提供请求的页面时出现问题。",
"Error_Page_400_Description_1": "通常这意味着在处理您的请求时发生了意外错误。",
"Error_Page_400_Description_2": "如果问题仍然存在,请通过 <a href=\"mailto:info@abp.io\">info@abp.io</a> 联系我们,我们将帮助您开始。",
"GoToHomepage": "去首页",
"Error_Page_404_Title": "页面未找到!",
"Error_Page_404_Description_1": "这不是您要查找的网页。",
"Error_Page_500_Title": "好像出了什么问题!",
"Error_Page_500_Description_1": "我们会自动跟踪这些错误,但如果问题仍然存在,请随时 <br /> 联系我们。 与此同时,尝试刷新。",
"Error_Page_500_Description_2": "通过 <a href=\"mailto:info@abp.io\" target=\"_blank\">info@abp.io</a> 与我们联系。",
"Books": "书籍"
} }
} }

2
abp_io/AbpIoLocalization/AbpIoLocalization/Blog/Localization/Resources/zh-Hans.json

@ -1,5 +1,7 @@
{ {
"culture": "zh-Hans", "culture": "zh-Hans",
"texts": { "texts": {
"AbpTitle": "ABP 框架 - 开源 Web 应用程序框架",
"AbpDescription": "ABP 是一个开源应用程序框架,专注于基于 AspNet Core 的 Web 应用程序开发。 Don't repeat yourself,专注于自己的业务代码。"
} }
} }

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

@ -536,6 +536,7 @@
"Pricing_Page_Testimonial_2": "We are seeing the value of using ABP Commercial to reduce the overhead of custom development projects. And the team is able to unify the code pattern in different project streams. We see more potential in the framework for us to build new features faster than before. We trust we will be constantly seeing the value of leveraging ABP Commercial.", "Pricing_Page_Testimonial_2": "We are seeing the value of using ABP Commercial to reduce the overhead of custom development projects. And the team is able to unify the code pattern in different project streams. We see more potential in the framework for us to build new features faster than before. We trust we will be constantly seeing the value of leveraging ABP Commercial.",
"Pricing_Page_Testimonial_3": "We love ABP. We don't have to write everything from scratch. We start from out-of-the-box features and just focus on what we really need to write. Also, ABP is well-architected and the code is high quality with fewer bugs. If we would have to write everything we needed on our own, we might have to spend years. Once more things we like is that the new version, or issue fixing, or improvement come out very soon every other week. We don't wait too long.", "Pricing_Page_Testimonial_3": "We love ABP. We don't have to write everything from scratch. We start from out-of-the-box features and just focus on what we really need to write. Also, ABP is well-architected and the code is high quality with fewer bugs. If we would have to write everything we needed on our own, we might have to spend years. Once more things we like is that the new version, or issue fixing, or improvement come out very soon every other week. We don't wait too long.",
"Pricing_Page_Testimonial_4": "ABP Commercial is a fantastic product would recommend. Commercial products to market for our customers in a single configurable platform. The jump start that the framework and tooling provide any team is worth every cent. ABP Commercial was the best fit for our needs.", "Pricing_Page_Testimonial_4": "ABP Commercial is a fantastic product would recommend. Commercial products to market for our customers in a single configurable platform. The jump start that the framework and tooling provide any team is worth every cent. ABP Commercial was the best fit for our needs.",
"Pricing_Page_Testimonial_5": "ABP Framework is not only a framework, but it is also a guide for project development/management, because it provides DDD, GenericRepository, DI, Microservice, and Modularity training. Even if you are not going to use the framework itself, you can develop yourself with docs.abp.io which is well and professionally prepared (OpenIddict, Redis, Quartz etc.). Because many things are pre-built, it shortens project development time significantly (Such as login page, exception handling, data filtering, seeding, audit logging, localization, auto API controller etc.). As an example from our application, I have used Local Event Bus for stock control. So, I am able to manage order movements by writing stock handler. It is wonderful not to lose time for CreationTime, CreatorId. They are being filled automatically.",
"AbpBookDownloadArea_ClaimYourEBook": "Claim your <span class='gradient-framework'>Mastering ABP Framework</span> E-Book", "AbpBookDownloadArea_ClaimYourEBook": "Claim your <span class='gradient-framework'>Mastering ABP Framework</span> E-Book",
"AddMemberModal_Warning_1": "If the <strong>username</strong> you are trying to add doesn't exist in the system, please ask your team member to register on <a href='{0}/Account/Register'>{0}</a> and share the username of his/her account with you.", "AddMemberModal_Warning_1": "If the <strong>username</strong> you are trying to add doesn't exist in the system, please ask your team member to register on <a href='{0}/Account/Register'>{0}</a> and share the username of his/her account with you.",
"MyOrganizations_Detail_WelcomeMessage": "Welcome to your organization, {0}", "MyOrganizations_Detail_WelcomeMessage": "Welcome to your organization, {0}",
@ -730,7 +731,10 @@
"Welcome_Page_UseSameCredentialForCommercialWebsites": "Use the same credentials for both <a href=\"https://commercial.abp.io\" class=\"text-primary\">commercial.abp.io</a> and <a href=\"https://support.abp.io\" class=\"text-primary\">support.abp.io</a>.", "Welcome_Page_UseSameCredentialForCommercialWebsites": "Use the same credentials for both <a href=\"https://commercial.abp.io\" class=\"text-primary\">commercial.abp.io</a> and <a href=\"https://support.abp.io\" class=\"text-primary\">support.abp.io</a>.",
"WatchCrudPagesVideo": "Watch the \"Creating CRUD Pages with ABP Suite\" Video!", "WatchCrudPagesVideo": "Watch the \"Creating CRUD Pages with ABP Suite\" Video!",
"WatchGeneratingFromDatabaseVideo": "Watch the \"ABP Suite: Generating CRUD Pages From Existing Database Tables\" Video!", "WatchGeneratingFromDatabaseVideo": "Watch the \"ABP Suite: Generating CRUD Pages From Existing Database Tables\" Video!",
"WatchTakeCloserLookVideo": "Watch the \"Take a closer look at the code generation: ABP Suite\" Video!" "WatchTakeCloserLookVideo": "Watch the \"Take a closer look at the code generation: ABP Suite\" Video!",
"ConfirmedEmailAddressRequiredToStartTrial": "You should have a confirmed email address in order to start a trial license.",
"EmailVerificationMailNotSent": "Email verification mail couldn't send.",
"GetConfirmationEmail": "<a href=\"javascript:void(0);\" id=\"{0}\">Click here to get a confirmation email</a> if you haven't got it before.",
"WhichLicenseTypeYouAreInterestedIn": "Which license type you are interested in?"
} }
} }

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

@ -29,14 +29,12 @@
"ContributionGuide": "贡献指南", "ContributionGuide": "贡献指南",
"BugReport": "Bug报告", "BugReport": "Bug报告",
"SeeAllPosts": "查看所有的文章", "SeeAllPosts": "查看所有的文章",
"WelcomeToABPCommunity!": "欢迎来到ABP社区!", "WelcomeToABP": "欢迎来到ABP",
"MyProfile": "我的资料",
"MyOrganizations": "我的组织",
"EmailNotValid": "请输入有效的电子邮箱地址.", "EmailNotValid": "请输入有效的电子邮箱地址.",
"FeatureRequest": "功能请求", "FeatureRequest": "功能请求",
"CreatePostTitleInfo": "文章标题显示在文章列表中.", "CreatePostTitleInfo": "文章标题显示在文章列表中.",
"CreatePostSummaryInfo": "文章的简短摘要将显示在文章列表中.", "CreatePostSummaryInfo": "文章的简短摘要将显示在文章列表中.",
"CreatePostCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!", "CreatePostCoverInfo": "要创建一个有效的帖子,需要添加封面图片. 上传16:9的图片获取最佳视觉效果. 文件最大支持: 1MB",
"ThisExtensionIsNotAllowed": "不允许此扩展名.", "ThisExtensionIsNotAllowed": "不允许此扩展名.",
"TheFileIsTooLarge": "文件过大.", "TheFileIsTooLarge": "文件过大.",
"GoToThePost": "转到文章", "GoToThePost": "转到文章",
@ -45,7 +43,7 @@
"Done": "完成", "Done": "完成",
"Open": "打开", "Open": "打开",
"Closed": "关闭", "Closed": "关闭",
"LatestQuestionOnThe": "有关的最新问题", "RecentQuestionFrom": "最近的问题来自",
"Stackoverflow": "Stackoverflow", "Stackoverflow": "Stackoverflow",
"Votes": "票数", "Votes": "票数",
"Answer": "回答", "Answer": "回答",
@ -59,7 +57,7 @@
"QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.", "QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.",
"Oops": "哎呀!", "Oops": "哎呀!",
"CreatePostSuccessMessage": "文章提交成功. 网站管理员审核通过后将被发布.", "CreatePostSuccessMessage": "文章提交成功. 网站管理员审核通过后将被发布.",
"ChooseCoverImage": "选项一张封面图片", "Browse": "浏览",
"CoverImage": "封面图片", "CoverImage": "封面图片",
"ShareYourExperiencesWithTheABPFramework": "分享你的ABP Framework经验!", "ShareYourExperiencesWithTheABPFramework": "分享你的ABP Framework经验!",
"Optional": "可选的", "Optional": "可选的",
@ -88,6 +86,8 @@
"PostRequestFromGithubIssue": "现在没有任何文章请求.", "PostRequestFromGithubIssue": "现在没有任何文章请求.",
"LatestPosts": "最新的帖子", "LatestPosts": "最新的帖子",
"ArticleRequests": "文章请求", "ArticleRequests": "文章请求",
"ArticleRequestsDescription": "想在这里查看具体内容吗? 您可以要求社区创建它!",
"LatestContentRequests": "最新内容请求",
"AllPostRequests": "查看所有文章请求", "AllPostRequests": "查看所有文章请求",
"SubscribeToTheNewsletter": "订阅简讯", "SubscribeToTheNewsletter": "订阅简讯",
"NewsletterEmailDefinition": "获取有关ABP发生的信息,例如新版本,免费资源,文章等.", "NewsletterEmailDefinition": "获取有关ABP发生的信息,例如新版本,免费资源,文章等.",
@ -115,7 +115,6 @@
"VideoUrl": "视频Url", "VideoUrl": "视频Url",
"GithubPostUrl": "Github文章Url", "GithubPostUrl": "Github文章Url",
"ExternalPostUrl": "外部文章Url", "ExternalPostUrl": "外部文章Url",
"CreatePostCoverInfo": "要创建一个有效的帖子,需要添加封面图片. 上传16:9的图片获取最佳视觉效果. 文件最大支持: 1MB",
"ThankYouForContribution": "感谢你对ABP社区的贡献", "ThankYouForContribution": "感谢你对ABP社区的贡献",
"GithubPost": "Github文章", "GithubPost": "Github文章",
"GithubPostSubmitStepOne": "<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>", "GithubPostSubmitStepOne": "<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>",
@ -143,6 +142,48 @@
"Volo.AbpIo.Domain:060001": "源 URL(\"{PostUrl}\") 不是 Github URL", "Volo.AbpIo.Domain:060001": "源 URL(\"{PostUrl}\") 不是 Github URL",
"Volo.AbpIo.Domain:060002": "文章内容无法从 Github(\"{PostUrl}\") 资源中获得。", "Volo.AbpIo.Domain:060002": "文章内容无法从 Github(\"{PostUrl}\") 资源中获得。",
"Volo.AbpIo.Domain:060003": "没有找到文章内容!", "Volo.AbpIo.Domain:060003": "没有找到文章内容!",
"SeeMore": "查看更多" "SeeMore": "查看更多",
"JoinTheABPCommunity": "加入 ABP 社区",
"ABPCommunityTalks": "ABP 社区会谈",
"LiveDemo": "现场演示",
"GetLicense": "获得许可证",
"GetStarted": "开始使用",
"SourceCode": "源代码",
"LeaveComment": "发表评论",
"ReadMore": "阅读更多",
"ShowMore": "展示更多",
"NoPublishedPostsYet": "还没有发布的帖子。",
"Name": "名字",
"Surname": "姓氏",
"WebSite": "网站",
"FullURL": "完整网址",
"JobTitle": "职称",
"Prev": "上一页",
"Previous": "前面的",
"Next": "下一页",
"Share": "分享",
"SortBy": "排序方式",
"NoPublishedEventsYet": "尚未发布活动。",
"SubscribeYoutubeChannel": "订阅 Youtube 频道",
"Enum:EventType:0": "会谈",
"MemberNotPublishedPostYet": "该成员尚未发布任何帖子。",
"TimeAgo": "{0} 前",
"Discord_Page_JoinCommunityMessage": "加入 ABP Discord 社区",
"Discord_Page_Announce": "我们很高兴地宣布 ABP 社区 Discord 服务器!",
"Discord_Page_Description_1": "ABP 社区从第一天起就一直在壮大。 我们希望通过创建一个官方的 ABP Discord 服务器将其带入下一步,这样 ABP 社区就可以使用即时消息的功能彼此进行互动。",
"Discord_Page_Description_2": "ABP Community Discord Server 是您可以使用 ABP 框架来展示您的创作、分享对您有用的技巧、了解有关 ABP 框架的最新消息和公告、与社区成员聊天交流想法并玩得开心的地方!",
"Discord_Page_Description_3": "此 ABP 社区 Discord 服务器是 ABP 核心团队的官方服务器,存在于服务器上进行监控。",
"Discord_Page_JoinToServer": "加入 ABP Discord 服务器",
"Events_Page_MetaTitle": "ABP 社区活动",
"Events_Page_MetaDescription": "现场活动由 ABP 团队主持,是充满社区内容、演示、问答和围绕 ABP 正在发生的事情的讨论的休闲会议。",
"Events_Page_Title": "ABP<span class=\"gradient-community\">社区</span>会谈",
"Events_Page_WritingFromUser": "阅读来自 ABP 社区的 {0} 的文章。",
"Post_Create_Page_MetaTitle": "最新帖子",
"Post_Create_Page_MetaDescription": "创建您的帖子以分享您对 ABP 框架的经验并为 ABP 社区做出贡献。",
"Post_Create_Page_CreateNewPost": "创建新帖子",
"Post_Index_Page_MetaDescription": "ABP 社区的目的是为使用 ABP 框架的开发人员创建一个贡献环境。",
"Layout_Title": "{0} | ABP 社区",
"Layout_MetaDescription": "ABP 社区是一个人们可以分享有关 ABP 框架的帖子并关注项目的环境。",
"Index_Page_CommunityIntroduction": "这是 ABP 框架、.NET 和软件开发的中心。 您可以阅读文章,观看视频教程,了解 ABP 的开发进度和 ABP 相关事件,帮助其他开发人员并与 ABP 社区分享您的专业知识。"
} }
} }

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

@ -174,6 +174,7 @@
"CreateProjectWizard": "此向导让你从启动模板创建一个新项目,该启动模板已正确配置为可以快速启动你的项目.", "CreateProjectWizard": "此向导让你从启动模板创建一个新项目,该启动模板已正确配置为可以快速启动你的项目.",
"TieredOption": "创建一个分层解决方案,其中Web和Http API层在物理上是分离的. 如果没有选中则创建一个不那么复杂且适合大多数场景的分层解决方案.", "TieredOption": "创建一个分层解决方案,其中Web和Http API层在物理上是分离的. 如果没有选中则创建一个不那么复杂且适合大多数场景的分层解决方案.",
"SeparateIdentityServerOption": "将服务器端分离为两个应用程序:第一个应用程序用于身份服务器,第二个应用程序用于服务器端HTTP API.", "SeparateIdentityServerOption": "将服务器端分离为两个应用程序:第一个应用程序用于身份服务器,第二个应用程序用于服务器端HTTP API.",
"ProgressiveWebApplicationOption": "将项目指定为渐进式 Web 应用程序",
"UseslatestPreVersion": "使用最新的预发布版本", "UseslatestPreVersion": "使用最新的预发布版本",
"ReadTheDocumentation": "<span class=\"text-primary\">阅读</span><span class=\"text-success\">文档</span>", "ReadTheDocumentation": "<span class=\"text-primary\">阅读</span><span class=\"text-success\">文档</span>",
"Documentation": "文档", "Documentation": "文档",
@ -214,7 +215,11 @@
"SeeDocs": "查看文档", "SeeDocs": "查看文档",
"None": "空", "None": "空",
"Application": "应用程序", "Application": "应用程序",
"ApplicationExplanation": "基于领域驱动设计实践创建一个完全分层的解决方案。 推荐用于需要可维护和可扩展代码库的长期项目。",
"ApplicationNoLayer": "应用程序(单层)",
"ApplicationNoLayerExplanation": "创建单层 Web 应用程序。 推荐用于构建具有更简单且易于理解的架构的应用程序。",
"Module": "模块", "Module": "模块",
"ModuleExplanation": "创建可重用、完全分层的应用程序模块解决方案。 您可以使用此选项为您的模块化应用程序创建模块。",
"PackageName": "包名称", "PackageName": "包名称",
"LicenseURL": "许可URL", "LicenseURL": "许可URL",
"License": "许可", "License": "许可",
@ -273,6 +278,100 @@
"SubscribeToNewsletter": "订阅时事通讯以获取有关 ABP.IO 平台中发生的事件的信息,例如新版本、文章、优惠等。", "SubscribeToNewsletter": "订阅时事通讯以获取有关 ABP.IO 平台中发生的事件的信息,例如新版本、文章、优惠等。",
"FirstEdition": "第一版", "FirstEdition": "第一版",
"ThankYou": "谢谢!", "ThankYou": "谢谢!",
"CheckboxMandatory": "你需要检查这个才能继续!" "CheckboxMandatory": "你需要检查这个才能继续!",
"UserInterface": "用户界面",
"APIGateway": "API 网关",
"Database": "数据库",
"Saas": "Saas",
"OpenSourceWebApp": "打开 <span class=\"forever\">source </span> <br/> Web 应用程序",
"Framework": "框架",
"AuditLoggingExplanation": "自动跟踪系统中的所有操作和数据更改。",
"AbpNewCommandExplanation": "使用 ABP 启动模板创建新的解决方案。",
"AbpAddModuleCommandExplanation": "将预构建的应用程序模块安装到您的解决方案中",
"AbpUpdateCommandExplanation": "自动更新解决方案中所有与 ABP 相关的 NuGet 和 NPM 包。",
"ExploreAllCLICommands": "探索所有 CLI 命令",
"ExploreDocumentationAndGuides": "探索全面的文档和指南。",
"Documentations": "文档",
"Views": "意见",
"ReadMore": "阅读更多",
"EnterYouEmailToGetNews": "输入您的电子邮件以获取有关 ABP 框架的最新消息",
"Tiered": "分层",
"SeparateIdentityServer": "独立的身份服务器",
"ProgressiveWebApplication": "渐进式 Web 应用程序",
"Preview": "预览",
"CreateANewSolution": "创建一个新的解决方案",
"ABPFrameworkFeatures": "ABP 框架 <span class=\"gradient-framework\">功能</span>",
"Commercial": "商业版",
"ThirdPartyTools": "第三方工具",
"Back": "后退",
"Community": "社区",
"SeeMore": "查看更多",
"DetailsOfTheEBook": "电子书详情",
"JoinOurMarketingNewsletter": "加入我们的市场推广时事通讯",
"FrameworkNewsletterConfirmationMessage": "我同意<a class=\"text-white fw-6 text-decoration-underline opacity-50\" href=\"https://commercial.abp.io/TermsConditions\">条款和条件</a > 和 <a class=\"text-white fw-6 text-decoration-underline opacity-50\" href=\"https://commercial.abp.io/Privacy\">隐私政策</a>。",
"GetYourFreeEBook": "获取您的 <span class=\"gradient-framework d-block\">免费 DDD 电子书</span>",
"EverythingYouNeedToKnow": "您需要了解的一切。",
"PreOrderNow": "立即预订",
"UITheming": "用户界面主题",
"UIThemingExplanation": "创建可重用的 UI 主题和布局,或使用预构建的 UI 主题之一。",
"DataFilteringExplanation2": "自动过滤来自数据库的查询,以轻松实现软删除和多租户等模式。",
"NeedHelp": "您需要帮助吗?",
"GiveYourProjectAName": "给您的项目命名",
"SelectProjectType": "选择项目类型",
"SelectUIFramework": "选择 UI 框架",
"SelectDatabaseProvider": "选择数据库提供者",
"SelectDatabaseManagementSystem": "选择数据库管理系统",
"InstallingTheABPCLI": "安装 ABP CLI",
"CreateYourProjectNow": "立即创建您的项目",
"OrderOn": "在 {0} 订购",
"DownloadFreeDDDBook": "下载免费的 DDD 书",
"WhatIsABPFramework": "什么是 ABP 框架?",
"TenantDatabase": "租户 {0} 数据库",
"SharedDatabase": "共享数据库",
"ConnectionResolver": "连接解析器",
"TenantBasedDataFilter": "基于租户的数据过滤器",
"ApplicationCode": "申请代码",
"TenantResolution": "租户决议",
"TenantUser": "租户 {0} 用户",
"CardTitle": "卡片标题",
"View": "查看",
"Model": "模型",
"Email": "电子邮箱",
"Password": "密码",
"Address": "地址",
"Gender": "性别",
"Male": "男",
"Female": "女",
"Submit": "提交",
"Unspecified": "未指定",
"StaticFileMiddleware": "静态文件中间件",
"RazorViewEngine": "Razor 视图引擎",
"PhysicalFiles": "物理文件 (wwwroot)",
"EmbeddedFiles": "嵌入式文件(DDL) ",
"DynamicFiles": "动态文件(内存)",
"BuildSolutionsWithAbp": "遵循使用 ABP 的软件开发最佳实践,构建可维护的 .NET 解决方案。",
"BuyOnAmazon": "在亚马逊上购买",
"BuyOnPackt": "在 Packt 上购买",
"Discounted": "打折",
"MasteringAbpFramework_Book_KeyFeatures": "主要特性",
"MasteringAbpFramework_Book_Key_Features_Description_1": "使用 ABP 框架构建强大、可维护、模块化和可扩展的软件解决方案。",
"MasteringAbpFramework_Book_Key_Features_Description_2": "了解如何在您的 Web 应用程序中实施 SOLID 原则和领域驱动设计。",
"MasteringAbpFramework_Book_Key_Features_Description_3": "了解 ABP 框架如何通过自动执行重复性任务来加快您的开发周期。",
"MasteringAbpFramework_Book_Description": "书籍说明",
"MasteringAbpFramework_Book_Description_Details_1": "ABP 框架是一个完整的基础架构,用于通过遵循软件 \n 开发最佳实践和约定来创建现代 Web 应用程序。 借助 ABP 的高级框架和生态系统,您可以 \n 实现 Don't Repeat Yourself (DRY) 原则并专注于您的业务代码。",
"MasteringAbpFramework_Book_Description_Details_2": "本书由 ABP 框架的创建者撰写,将帮助您全面了解该框架和现代 Web 应用程序开发技术。 通过对基本概念和实际示例的逐步解释,您将了解现代 Web 解决方案的要求以及 ABP\n 框架如何使开发您自己的解决方案变得轻松愉快。 您将发现\n 企业 Web 应用程序开发的常见需求,并探索 ABP 提供的基础架构。 在整本书中,您将掌握构建可维护和模块化 Web 解决方案的软件开发最佳实践。",
"MasteringAbpFramework_Book_Description_Details_3": "在本书结束时,您将能够创建一个易于开发、\n 维护和测试的完整 Web 解决方案。",
"MasteringAbpFramework_Book_WhatYouWillLearn": "你将会学到什么",
"MasteringAbpFramework_Book_What_You_Will_Learn_1": "设置开发环境并开始使用 ABP 框架。",
"MasteringAbpFramework_Book_What_You_Will_Learn_2": "使用 Entity Framework Core 和 MongoDB 开发您的数据访问层。",
"MasteringAbpFramework_Book_What_You_Will_Learn_3": "了解横切关注点以及 ABP 如何自动执行重复性任务。",
"MasteringAbpFramework_Book_What_You_Will_Learn_4": "掌握使用 ABP 框架实施领域驱动设计。",
"MasteringAbpFramework_Book_What_You_Will_Learn_5": "使用 ASP.NET Core MVC (Razor Pages) 和 Blazor 构建 UI 页面和组件。",
"MasteringAbpFramework_Book_What_You_Will_Learn_6": "使用多租户创建模块化 Web 应用程序。",
"MasteringAbpFramework_Book_What_You_Will_Learn_7": "了解模块化并创建可重用的应用程序模块。",
"MasteringAbpFramework_Book_What_You_Will_Learn_8": "使用 ABP 框架编写单元、集成和 UI 测试。",
"MasteringAbpFramework_Book_WhoIsThisBookFor": "这本书是给谁看的",
"MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "本书适用于希望学习软件架构和最佳实践的 Web 开发人员,以使用 Microsoft 技术和 ABP 框架构建\n 可维护的基于 Web 的解决方案。 C#\n 和 ASP.NET Core 的基本知识是开始阅读本书所必需的。",
"ComputersAndTechnology": "计算机与技术"
} }
} }

1
docs/en/CLI.md

@ -34,6 +34,7 @@ Here, is the list of all available commands before explaining their details:
* **`add-package`**: Adds an ABP package to a project. * **`add-package`**: Adds an ABP package to a project.
* **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution. * **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution.
* **`list-modules`**: Lists names of open-source application modules. * **`list-modules`**: Lists names of open-source application modules.
* **`list-templates`**: Lists the names of available templates to create a solution.
* **`get-source`**: Downloads the source code of a module. * **`get-source`**: Downloads the source code of a module.
* **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints. * **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints.
* **`remove-proxy`**: Removes previously generated client side proxies. * **`remove-proxy`**: Removes previously generated client side proxies.

16
docs/en/Multi-Tenancy.md

@ -32,6 +32,22 @@ Configure<AbpMultiTenancyOptions>(options =>
> Multi-Tenancy is disabled in the ABP Framework by default. However, it is **enabled by default** when you create a new solution using the [startup template](Startup-Templates/Application.md). `MultiTenancyConsts` class in the solution has a constant to control it in a single place. > Multi-Tenancy is disabled in the ABP Framework by default. However, it is **enabled by default** when you create a new solution using the [startup template](Startup-Templates/Application.md). `MultiTenancyConsts` class in the solution has a constant to control it in a single place.
### AbpMultiTenancyOptions: Handle inactive and non-existent tenants.
The `MultiTenancyMiddlewareErrorPageBuilder` of `AbpMultiTenancyOptions` is used to handle inactive and non-existent tenants.
It will respond to an error page by default, you can change it if you want, eg: only output the error log and continue ASP NET Core's request pipeline.
```csharp
Configure<AbpMultiTenancyOptions>(options =>
{
options.MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) =>
{
// Handle the exception.
};
});
```
### Database Architecture ### Database Architecture
ABP Framework supports all the following approaches to store the tenant data in the database; ABP Framework supports all the following approaches to store the tenant data in the database;

0
docs/en/Themes/LeptonXLite/mvc.md → docs/en/Themes/LeptonXLite/AspNetCore.md

2
docs/en/Themes/LeptonXLite/angular.md

@ -79,4 +79,4 @@ To change the logos and brand color of `LeptonX`, simply add the following CSS t
### Server Side ### Server Side
In order to migrate to LeptonX on your server side projects (Host and/or AuthServer projects), please follow the [Server Side Migration](mvc.md) document. In order to migrate to LeptonX on your server side projects (Host and/or AuthServer projects), please follow the [Server Side Migration](AspNetCore.md) document.

5
docs/en/Themes/LeptonXLite/blazor.md

@ -16,7 +16,8 @@ LeptonX Lite has implementation for the ABP Framework Blazor WebAssembly & Blazo
## Installation ## Installation
{{if UI == "Blazor"}} {{if UI == "Blazor"}}
- Complete the [MVC Razor Pages Installation](mvc.md#installation) for the **HttpApi.Host** application first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and if identity server is separated, install to the **OpenIddict**_. - Complete the [MVC Razor Pages Installation](AspNetCore.md#installation) for the **HttpApi.Host** application first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and if identity server is separated, install to the **OpenIddict**_.
- Add **Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme** package to your **Blazor WebAssembly** application with the following command: - Add **Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme** package to your **Blazor WebAssembly** application with the following command:
@ -52,7 +53,7 @@ builder.RootComponents.Add<App>("#ApplicationContainer");
{{if UI == "BlazorServer"}} {{if UI == "BlazorServer"}}
- Complete the [MVC Razor Pages Installation](mvc.md#installation) first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and **AuthServer**_. - Complete the [MVC Razor Pages Installation](AspNetCore.md#installation) first. _If the solution is tiered/micro-service, complete the MVC steps for all MVC applications such as **HttpApi.Host** and **AuthServer**_.
- Add **Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme** package to your **Blazor server** application with the following command: - Add **Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme** package to your **Blazor server** application with the following command:

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

@ -27,7 +27,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**. * The [Basic Theme](Basic-Theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license. * The [Lepton Theme](https://commercial.abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP Commercial](https://commercial.abp.io/) license.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/blazor) and a [lite](../../Themes/LeptonXLite/blazor.md) version. * The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/commercial/blazor) and a [lite](../../Themes/LeptonXLite/Blazor.md) version.
## Overall ## Overall

6
docs/en/docs-nav.json

@ -721,7 +721,7 @@
}, },
{ {
"text": "LeptonX Lite", "text": "LeptonX Lite",
"path": "Themes/LeptonXLite/mvc.md" "path": "Themes/LeptonXLite/AspNetCore.md"
} }
] ]
}, },
@ -837,7 +837,7 @@
}, },
{ {
"text": "LeptonX Lite", "text": "LeptonX Lite",
"path": "Themes/LeptonXLite/blazor.md" "path": "Themes/LeptonXLite/Blazor.md"
}, },
{ {
"text": "Branding", "text": "Branding",
@ -1112,7 +1112,7 @@
}, },
{ {
"text": "LeptonX Lite", "text": "LeptonX Lite",
"path": "Themes/LeptonXLite/angular.md" "path": "Themes/LeptonXLite/Angular.md"
} }
] ]
}, },

16
docs/zh-Hans/Multi-Tenancy.md

@ -34,6 +34,22 @@ namespace MyCompany.MyProject
> 随着"Multi-tenancy ready"的概念,我们打算开发我们的代码和多租户方法兼容.然后它可以被用于多租户和非多租户的程序中,这取决于最终程序的需求. > 随着"Multi-tenancy ready"的概念,我们打算开发我们的代码和多租户方法兼容.然后它可以被用于多租户和非多租户的程序中,这取决于最终程序的需求.
### AbpMultiTenancyOptions: 处理不活跃或不存在的租户
`MultiTenancyMiddlewareErrorPageBuilder` 或 `AbpMultiTenancyOptions` 用于 处理不活跃或不存在的租户.
默认情况下会响应错误页面, 你可以根据自己的需要更改它, 比如: 只输出错误日志并继续ASP NET Core的请求管道
```csharp
Configure<AbpMultiTenancyOptions>(options =>
{
options.MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) =>
{
// Handle the exception.
};
});
```
#### 定义实体 #### 定义实体
你可以在你的实体中实现 **IMultiTenant** 接口来实现多租户,例如: 你可以在你的实体中实现 **IMultiTenant** 接口来实现多租户,例如:

15
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyOptions.cs

@ -3,6 +3,8 @@ using System.Globalization;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.AspNetCore.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy;
@ -21,6 +23,15 @@ public class AbpAspNetCoreMultiTenancyOptions
TenantKey = TenantResolverConsts.DefaultTenantKey; TenantKey = TenantResolverConsts.DefaultTenantKey;
MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) => MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) =>
{ {
// Try to delete the tenant's cookie if it does not exist or is inactive.
var tenantResolveResult = context.RequestServices.GetRequiredService<ITenantResolveResultAccessor>().Result;
if (tenantResolveResult != null &&
tenantResolveResult.AppliedResolvers.Contains(CookieTenantResolveContributor.ContributorName))
{
var options = context.RequestServices.GetRequiredService<IOptions<AbpAspNetCoreMultiTenancyOptions>>().Value;
AbpMultiTenancyCookieHelper.SetTenantCookie(context, null, options.TenantKey);
}
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ;
context.Response.ContentType = "text/html"; context.Response.ContentType = "text/html";
@ -31,8 +42,8 @@ public class AbpAspNetCoreMultiTenancyOptions
await context.Response.WriteAsync($"<h3>{message}</h3>{details}<br>\r\n"); await context.Response.WriteAsync($"<h3>{message}</h3>{details}<br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n"); await context.Response.WriteAsync("</body></html>\r\n");
// Note the 500 spaces are to work around an IE 'feature' // Note the 500 spaces are to work around an IE 'feature'
await context.Response.WriteAsync(new string(' ', 500)); await context.Response.WriteAsync(new string(' ', 500));
}; };
} }
} }

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -49,6 +49,7 @@ public class AbpCliCoreModule : AbpModule
options.Commands[AddPackageCommand.Name] = typeof(AddPackageCommand); options.Commands[AddPackageCommand.Name] = typeof(AddPackageCommand);
options.Commands[AddModuleCommand.Name] = typeof(AddModuleCommand); options.Commands[AddModuleCommand.Name] = typeof(AddModuleCommand);
options.Commands[ListModulesCommand.Name] = typeof(ListModulesCommand); options.Commands[ListModulesCommand.Name] = typeof(ListModulesCommand);
options.Commands[ListTemplatesCommand.Name] = typeof(ListTemplatesCommand);
options.Commands[LoginCommand.Name] = typeof(LoginCommand); options.Commands[LoginCommand.Name] = typeof(LoginCommand);
options.Commands[LoginInfoCommand.Name] = typeof(LoginInfoCommand); options.Commands[LoginInfoCommand.Name] = typeof(LoginInfoCommand);
options.Commands[LogoutCommand.Name] = typeof(LogoutCommand); options.Commands[LogoutCommand.Name] = typeof(LogoutCommand);

106
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListTemplatesCommand.cs

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands.Templates;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Threading;
namespace Volo.Abp.Cli.Commands;
public class ListTemplatesCommand : IConsoleCommand, ITransientDependency
{
public const string Name = "list-templates";
private readonly CliHttpClientFactory _cliHttpClientFactory;
private readonly IJsonSerializer _jsonSerializer;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly IRemoteServiceExceptionHandler _remoteServiceExceptionHandler;
public ILogger<ListTemplatesCommand> Logger { get; set; }
public ListTemplatesCommand(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
ICancellationTokenProvider cancellationTokenProvider,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler)
{
_cliHttpClientFactory = cliHttpClientFactory;
_jsonSerializer = jsonSerializer;
_cancellationTokenProvider = cancellationTokenProvider;
_remoteServiceExceptionHandler = remoteServiceExceptionHandler;
Logger = NullLogger<ListTemplatesCommand>.Instance;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var templateNameSpaceLength = 26;
var templateDisplayNameSpaceLength = 32;
var templates = await GetTemplatesAsync();
var output = new StringBuilder(Environment.NewLine + Environment.NewLine);
output.AppendLine(
$"{"Template".PadRight(templateNameSpaceLength)}" +
$"{"Template Name".PadRight(templateDisplayNameSpaceLength)}" +
"Document Url");
output.AppendLine(
$"{"--------".PadRight(templateNameSpaceLength)}" +
$"{"-------------".PadRight(templateDisplayNameSpaceLength)}" +
"------------");
output.AppendLine();
foreach (var template in templates)
{
output.AppendLine(
$"{template.Name?.PadRight(templateNameSpaceLength)}" +
$"{template.DisplayName?.PadRight(templateDisplayNameSpaceLength)}" +
$"{template.DocumentUrl}");
}
Logger.LogInformation(output.ToString());
}
private async Task<List<TemplateInfo>> GetTemplatesAsync()
{
var client = _cliHttpClientFactory.CreateClient();
using (var responseMessage = await client.GetAsync(
$"{CliUrls.WwwAbpIo}api/download/templates/",
_cancellationTokenProvider.Token
))
{
await _remoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(responseMessage);
var result = await responseMessage.Content.ReadAsStringAsync();
return _jsonSerializer.Deserialize<List<TemplateInfo>>(result);
}
}
public string GetUsageInfo()
{
var sb = new StringBuilder();
sb.AppendLine("");
sb.AppendLine("Usage:");
sb.AppendLine(" abp list-templates");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
return sb.ToString();
}
public string GetShortDescription()
{
return "Lists available templates to be created.";
}
}

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

@ -44,7 +44,7 @@ public abstract class ProjectCreationCommandBase
AngularPwaSupportAdder angularPwaSupportAdder, AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator, InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder, ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus) ILocalEventBus eventBus)
{ {
ConnectionStringProvider = connectionStringProvider; ConnectionStringProvider = connectionStringProvider;
SolutionPackageVersionFinder = solutionPackageVersionFinder; SolutionPackageVersionFinder = solutionPackageVersionFinder;
@ -149,17 +149,38 @@ public abstract class ProjectCreationCommandBase
SolutionName solutionName; SolutionName solutionName;
if (MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(template)) if (MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(template))
{ {
var slnFile = Directory.GetFiles(outputFolderRoot, "*.sln").FirstOrDefault(); var slnPath = commandLineArgs.Options.GetOrNull(Options.MainSolution.Short, Options.MainSolution.Long);
if (slnFile == null)
if (slnPath == null)
{
slnPath = Directory.GetFiles(outputFolderRoot, "*.sln").FirstOrDefault();
}
else if (slnPath.EndsWith(".sln"))
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(slnPath));
outputFolderRoot = Path.GetDirectoryName(slnPath);
}
else if (!Directory.Exists(slnPath))
{
slnPath = null;
}
else
{
Directory.SetCurrentDirectory(slnPath);
outputFolderRoot = slnPath;
slnPath = Directory.GetFiles(outputFolderRoot, "*.sln").FirstOrDefault();
}
if (slnPath == null)
{ {
throw new CliUsageException("This command should be run inside a folder that contains a microservice solution!"); throw new CliUsageException($"This command should be run inside a folder that contains a microservice solution! Or use -{Options.MainSolution.Short} parameter.");
} }
var microserviceSolutionName = Path.GetFileName(slnFile).RemovePostFix(".sln"); var microserviceSolutionName = Path.GetFileName(slnPath).RemovePostFix(".sln");
version ??= SolutionPackageVersionFinder.Find(slnFile); version ??= SolutionPackageVersionFinder.Find(slnPath);
solutionName = SolutionName.Parse(microserviceSolutionName, projectName); solutionName = SolutionName.Parse(microserviceSolutionName, projectName);
outputFolder = MicroserviceServiceTemplateBase.CalculateTargetFolder(outputFolderRoot, projectName); outputFolder = MicroserviceServiceTemplateBase.CalculateTargetFolder(outputFolderRoot, solutionName.ProjectName);
uiFramework = uiFramework == UiFramework.NotSpecified ? FindMicroserviceSolutionUiFramework(outputFolderRoot) : uiFramework; uiFramework = uiFramework == UiFramework.NotSpecified ? FindMicroserviceSolutionUiFramework(outputFolderRoot) : uiFramework;
} }
else else
@ -206,10 +227,11 @@ public abstract class ProjectCreationCommandBase
protected void ExtractProjectZip(ProjectBuildResult project, string outputFolder) protected void ExtractProjectZip(ProjectBuildResult project, string outputFolder)
{ {
EventBus.PublishAsync(new ProjectCreationProgressEvent { EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Extracting the solution archieve" Message = "Extracting the solution archieve"
}, false); }, false);
using (var templateFileStream = new MemoryStream(project.ZipContent)) using (var templateFileStream = new MemoryStream(project.ZipContent))
{ {
using (var zipInputStream = new ZipInputStream(templateFileStream)) using (var zipInputStream = new ZipInputStream(templateFileStream))
@ -345,10 +367,11 @@ public abstract class ProjectCreationCommandBase
{ {
if (MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(projectArgs.TemplateName)) if (MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(projectArgs.TemplateName))
{ {
await EventBus.PublishAsync(new ProjectCreationProgressEvent { await EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Building the microservice solution" Message = "Building the microservice solution"
}, false); }, false);
CmdHelper.RunCmd("dotnet build /graphbuild", projectArgs.OutputFolder); CmdHelper.RunCmd("dotnet build /graphbuild", projectArgs.OutputFolder);
} }
} }
@ -361,11 +384,12 @@ public abstract class ProjectCreationCommandBase
MicroserviceServiceTemplateBase.IsMicroserviceTemplate(projectArgs.TemplateName)) MicroserviceServiceTemplateBase.IsMicroserviceTemplate(projectArgs.TemplateName))
{ {
Logger.LogInformation("Installing client-side packages..."); Logger.LogInformation("Installing client-side packages...");
await EventBus.PublishAsync(new ProjectCreationProgressEvent { await EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Installing client-side packages" Message = "Installing client-side packages"
}, false); }, false);
await InstallLibsService.InstallLibsAsync(projectArgs.OutputFolder); await InstallLibsService.InstallLibsAsync(projectArgs.OutputFolder);
} }
} }
@ -402,10 +426,11 @@ public abstract class ProjectCreationCommandBase
return; return;
} }
await EventBus.PublishAsync(new ProjectCreationProgressEvent { await EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Creating the initial DB migration" Message = "Creating the initial DB migration"
}, false); }, false);
await InitialMigrationCreator.CreateAsync(Path.GetDirectoryName(efCoreProjectPath), isLayeredTemplate); await InitialMigrationCreator.CreateAsync(Path.GetDirectoryName(efCoreProjectPath), isLayeredTemplate);
} }
@ -417,7 +442,7 @@ public abstract class ProjectCreationCommandBase
if (isAngular && isPwa) if (isAngular && isPwa)
{ {
Logger.LogInformation("Adding PWA Support to Angular app."); Logger.LogInformation("Adding PWA Support to Angular app.");
AngularPwaSupportAdder.AddPwaSupport(projectArgs.OutputFolder); AngularPwaSupportAdder.AddPwaSupport(projectArgs.OutputFolder);
} }
} }
@ -633,10 +658,16 @@ public abstract class ProjectCreationCommandBase
{ {
public const string Short = "pwa"; public const string Short = "pwa";
} }
public static class MainSolution
{
public const string Short = "ms";
public const string Long = "main-solution";
}
public static class Theme public static class Theme
{ {
public const string Long = "theme"; public const string Long = "theme";
} }
} }
} }

14
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Templates/TemplateInfo.cs

@ -0,0 +1,14 @@
namespace Volo.Abp.Cli.Commands.Templates;
public class TemplateInfo
{
public string Name { get; set; }
public string DisplayName { get; set; }
public string ShortDescription { get; set; }
public string DocumentUrl { get; set; }
public bool IsPro { get; set; }
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SolutionName.cs

@ -19,6 +19,8 @@ public class SolutionName
public static SolutionName Parse(string fullName, string microserviceName) public static SolutionName Parse(string fullName, string microserviceName)
{ {
microserviceName = microserviceName.Replace(".", string.Empty);
return new SolutionName(fullName + "." + microserviceName, fullName, microserviceName); return new SolutionName(fullName + "." + microserviceName, fullName, microserviceName);
} }

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs

@ -246,6 +246,17 @@ public class AngularSourceCodeAdder : ITransientDependency
foreach (var folder in angularPathsInDownloadedSourceCode) foreach (var folder in angularPathsInDownloadedSourceCode)
{ {
var nodeModulesFolder = Path.Combine(folder, "node_modules");
var vscodeFolder = Path.Combine(folder, ".vscode");
if (Directory.Exists(nodeModulesFolder))
{
Directory.Delete(nodeModulesFolder, true);
}
if (Directory.Exists(vscodeFolder))
{
Directory.Delete(vscodeFolder, true);
}
var projectsInFolder = Directory.GetDirectories(folder); var projectsInFolder = Directory.GetDirectories(folder);
if (projectsInFolder.Length == 1 && Path.GetFileName(projectsInFolder[0]) == "projects") if (projectsInFolder.Length == 1 && Path.GetFileName(projectsInFolder[0]) == "projects")
@ -286,7 +297,7 @@ public class AngularSourceCodeAdder : ITransientDependency
Directory.Move(projectInFolder, destDirName); Directory.Move(projectInFolder, destDirName);
} }
if (!Directory.GetFiles(folder).Any() && !Directory.GetDirectories(folder).Any()) if (!Directory.GetDirectories(folder).Any())
{ {
Directory.Delete(folder, true); Directory.Delete(folder, true);
} }

29
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -427,11 +427,6 @@ public class SolutionModuleAdder : ITransientDependency
await PublishEventAsync(9, $"Adding angular source code"); await PublishEventAsync(9, $"Adding angular source code");
if (newTemplate)
{
MoveAngularFolderInNewTemplate(modulesFolderInSolution, moduleName);
}
await AngularSourceCodeAdder.AddFromModuleAsync(solutionFilePath, angularPath); await AngularSourceCodeAdder.AddFromModuleAsync(solutionFilePath, angularPath);
} }
@ -449,30 +444,6 @@ public class SolutionModuleAdder : ITransientDependency
} }
} }
private static void MoveAngularFolderInNewTemplate(string modulesFolderInSolution, string moduleName)
{
var moduleAngularFolder = Path.Combine(modulesFolderInSolution, moduleName, "angular");
if (!Directory.Exists(moduleAngularFolder))
{
return;
}
var files = Directory.GetFiles(moduleAngularFolder);
var folders = Directory.GetDirectories(moduleAngularFolder);
Directory.CreateDirectory(Path.Combine(moduleAngularFolder, moduleName));
foreach (var file in files)
{
File.Move(file, Path.Combine(moduleAngularFolder, moduleName, Path.GetFileName(file)));
}
foreach (var folder in folders)
{
Directory.Move(folder, Path.Combine(moduleAngularFolder, moduleName, Path.GetFileName(folder)));
}
}
private async Task DownloadSourceCodesToSolutionFolder(ModuleWithMastersInfo module, private async Task DownloadSourceCodesToSolutionFolder(ModuleWithMastersInfo module,
string modulesFolderInSolution, string version = null, bool newTemplate = false, string modulesFolderInSolution, string version = null, bool newTemplate = false,
bool newProTemplate = false) bool newProTemplate = false)

94
framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs

@ -162,13 +162,13 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
protected async override Task PublishToEventBusAsync(Type eventType, object eventData) protected async override Task PublishToEventBusAsync(Type eventType, object eventData)
{ {
await PublishAsync( await PublishAsync(
AbpKafkaEventBusOptions.TopicName,
eventType, eventType,
eventData, eventData,
new Headers new Headers
{ {
{ "messageId", System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N")) } { "messageId", System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N")) }
}, }
null
); );
} }
@ -188,42 +188,31 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
new Headers new Headers
{ {
{ "messageId", System.Text.Encoding.UTF8.GetBytes(outgoingEvent.Id.ToString("N")) } { "messageId", System.Text.Encoding.UTF8.GetBytes(outgoingEvent.Id.ToString("N")) }
}, }
null
); );
} }
public override Task PublishManyFromOutboxAsync(IEnumerable<OutgoingEventInfo> outgoingEvents, OutboxConfig outboxConfig) public override Task PublishManyFromOutboxAsync(IEnumerable<OutgoingEventInfo> outgoingEvents, OutboxConfig outboxConfig)
{ {
var producer = ProducerPool.Get(); var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName);
var outgoingEventArray = outgoingEvents.ToArray(); var outgoingEventArray = outgoingEvents.ToArray();
producer.BeginTransaction();
try foreach (var outgoingEvent in outgoingEventArray)
{ {
foreach (var outgoingEvent in outgoingEventArray) var messageId = outgoingEvent.Id.ToString("N");
var headers = new Headers
{ {
var messageId = outgoingEvent.Id.ToString("N"); { "messageId", System.Text.Encoding.UTF8.GetBytes(messageId)}
var headers = new Headers };
{
{ "messageId", System.Text.Encoding.UTF8.GetBytes(messageId)}
};
producer.Produce(
AbpKafkaEventBusOptions.TopicName,
new Message<string, byte[]>
{
Key = outgoingEvent.EventName,
Value = outgoingEvent.EventData,
Headers = headers
});
}
producer.CommitTransaction(); producer.Produce(
} AbpKafkaEventBusOptions.TopicName,
catch (Exception e) new Message<string, byte[]>
{ {
producer.AbortTransaction(); Key = outgoingEvent.EventName,
throw; Value = outgoingEvent.EventData,
Headers = headers
});
} }
return Task.CompletedTask; return Task.CompletedTask;
@ -253,47 +242,22 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
return Serializer.Serialize(eventData); return Serializer.Serialize(eventData);
} }
public virtual async Task PublishAsync(Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments) private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers)
{
await PublishAsync(
AbpKafkaEventBusOptions.TopicName,
eventType,
eventData,
headers,
headersArguments
);
}
private Task PublishAsync(string topicName, Type eventType, object eventData, Headers headers, Dictionary<string, object> headersArguments)
{ {
var eventName = EventNameAttribute.GetNameOrDefault(eventType); var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData); var body = Serializer.Serialize(eventData);
return PublishAsync(topicName, eventName, body, headers, headersArguments); return PublishAsync(topicName, eventName, body, headers);
} }
private Task<DeliveryResult<string, byte[]>> PublishAsync( private Task<DeliveryResult<string, byte[]>> PublishAsync(
string topicName, string topicName,
string eventName, string eventName,
byte[] body, byte[] body,
Headers headers, Headers headers)
Dictionary<string, object> headersArguments)
{ {
var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName); var producer = ProducerPool.Get(AbpKafkaEventBusOptions.ConnectionName);
return PublishAsync(producer, topicName, eventName, body, headers, headersArguments);
}
private Task<DeliveryResult<string, byte[]>> PublishAsync(
IProducer<string, byte[]> producer,
string topicName,
string eventName,
byte[] body,
Headers headers,
Dictionary<string, object> headersArguments)
{
SetEventMessageHeaders(headers, headersArguments);
return producer.ProduceAsync( return producer.ProduceAsync(
topicName, topicName,
new Message<string, byte[]> new Message<string, byte[]>
@ -304,20 +268,6 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen
}); });
} }
private void SetEventMessageHeaders(Headers headers, Dictionary<string, object> headersArguments)
{
if (headersArguments == null)
{
return;
}
foreach (var header in headersArguments)
{
headers.Remove(header.Key);
headers.Add(header.Key, Serializer.Serialize(header.Value));
}
}
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType) private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
{ {
return HandlerFactories.GetOrAdd( return HandlerFactories.GetOrAdd(

10
framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/ProducerPool.cs

@ -41,16 +41,8 @@ public class ProducerPool : IProducerPool, ISingletonDependency
{ {
var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection)); var producerConfig = new ProducerConfig(Options.Connections.GetOrDefault(connection));
Options.ConfigureProducer?.Invoke(producerConfig); Options.ConfigureProducer?.Invoke(producerConfig);
return new ProducerBuilder<string, byte[]>(producerConfig).Build();
if (producerConfig.TransactionalId.IsNullOrWhiteSpace())
{
producerConfig.TransactionalId = Guid.NewGuid().ToString();
}
var producer = new ProducerBuilder<string, byte[]>(producerConfig).Build();
producer.InitTransactions(DefaultTransactionsWaitDuration);
return producer;
})).Value; })).Value;
} }

23
framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js

@ -1,24 +1,23 @@
var abp = abp || {}; var abp = abp || {};
(function () { (function() {
abp.SwaggerUIBundle = function (configObject) { abp.SwaggerUIBundle = function(configObject) {
var excludeUrl = ["swagger.json", "connect/token"] var excludeUrl = ["swagger.json", "connect/token"]
var firstRequest = true; var firstRequest = true;
abp.appPath = configObject.baseUrl || abp.appPath; abp.appPath = configObject.baseUrl || abp.appPath;
var requestInterceptor = configObject.requestInterceptor; var requestInterceptor = configObject.requestInterceptor;
configObject.requestInterceptor = async function (request) {
if(request.url.includes(excludeUrl[1])){ configObject.requestInterceptor = async function(request) {
if (request.url.includes(excludeUrl[1])) {
firstRequest = true; firstRequest = true;
} }
if(firstRequest && !excludeUrl.some(url => request.url.includes(url))) if (firstRequest && !excludeUrl.some(url => request.url.includes(url))) {
{ await fetch(`${abp.appPath}abp/Swashbuckle/SetCsrfCookie`, {
await fetch(`${abp.appPath}abp/Swashbuckle/SetCsrfCookie`,{
headers: request.headers headers: request.headers
}); });
firstRequest = false; firstRequest = false;
@ -28,8 +27,12 @@ var abp = abp || {};
if (antiForgeryToken) { if (antiForgeryToken) {
request.headers[abp.security.antiForgery.tokenHeaderName] = antiForgeryToken; request.headers[abp.security.antiForgery.tokenHeaderName] = antiForgeryToken;
} }
if(requestInterceptor){ if (!request.headers["X-Requested-With"]) {
request.headers["X-Requested-With"] = "XMLHttpRequest";
}
if (requestInterceptor) {
requestInterceptor(request); requestInterceptor(request);
} }
return request; return request;

26
modules/identity/src/Volo.Abp.Identity.AspNetCore/Microsoft/AspNetCore/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs

@ -0,0 +1,26 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Extensions.DependencyInjection;
public static class AbpAspNetCoreServiceCollectionExtensions
{
public static IServiceCollection ForwardIdentityAuthenticationForBearer(this IServiceCollection services, string jwtBearerScheme = "Bearer")
{
services.ConfigureApplicationCookie(options =>
{
options.ForwardDefaultSelector = ctx =>
{
string authorization = ctx.Request.Headers.Authorization;
if (!authorization.IsNullOrWhiteSpace() && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
return jwtBearerScheme;
}
return null;
};
});
return services;
}
}

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

@ -136,6 +136,7 @@ export class CoreModule {
return { return {
ngModule: RootCoreModule, ngModule: RootCoreModule,
providers: [ providers: [
OAuthModule.forRoot().providers,
LocaleProvider, LocaleProvider,
CookieLanguageProvider, CookieLanguageProvider,
{ {
@ -190,7 +191,6 @@ export class CoreModule {
useValue: localizationContributor(options.localizations), useValue: localizationContributor(options.localizations),
deps: [LocalizationService], deps: [LocalizationService],
}, },
OAuthModule.forRoot().providers,
], ],
}; };
} }

8
npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts

@ -18,6 +18,8 @@ import { PermissionService } from '../services/permission.service';
export class PermissionDirective implements OnDestroy, OnChanges { export class PermissionDirective implements OnDestroy, OnChanges {
@Input('abpPermission') condition: string | undefined; @Input('abpPermission') condition: string | undefined;
@Input('abpPermissionRunChangeDetection') runChangeDetection = true;
subscription!: Subscription; subscription!: Subscription;
constructor( constructor(
@ -38,7 +40,11 @@ export class PermissionDirective implements OnDestroy, OnChanges {
.subscribe(isGranted => { .subscribe(isGranted => {
this.vcRef.clear(); this.vcRef.clear();
if (isGranted) this.vcRef.createEmbeddedView(this.templateRef); if (isGranted) this.vcRef.createEmbeddedView(this.templateRef);
this.cdRef.detectChanges(); if (this.runChangeDetection) {
this.cdRef.detectChanges();
} else {
this.cdRef.markForCheck();
}
}); });
} }

50
npm/ng-packs/packages/core/src/lib/tests/permission.directive.spec.ts

@ -2,10 +2,12 @@ import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/je
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { PermissionDirective } from '../directives/permission.directive'; import { PermissionDirective } from '../directives/permission.directive';
import { PermissionService } from '../services'; import { PermissionService } from '../services';
import { ChangeDetectorRef } from '@angular/core';
describe('PermissionDirective', () => { describe('PermissionDirective', () => {
let spectator: SpectatorDirective<PermissionDirective>; let spectator: SpectatorDirective<PermissionDirective>;
let directive: PermissionDirective; let directive: PermissionDirective;
let cdr: ChangeDetectorRef;
const grantedPolicy$ = new Subject<boolean>(); const grantedPolicy$ = new Subject<boolean>();
const createDirective = createDirectiveFactory({ const createDirective = createDirectiveFactory({
directive: PermissionDirective, directive: PermissionDirective,
@ -30,7 +32,7 @@ describe('PermissionDirective', () => {
grantedPolicy$.next(true); grantedPolicy$.next(true);
expect(spectator.query('#test-element')).toBeTruthy(); expect(spectator.query('#test-element')).toBeTruthy();
grantedPolicy$.next(false); grantedPolicy$.next(false);
// expect(spectator.query('#test-element')).toBeFalsy(); // TODO: change detection problem should be fixed expect(spectator.query('#test-element')).toBeFalsy();
}); });
}); });
@ -41,6 +43,7 @@ describe('PermissionDirective', () => {
{ hostProps: { condition: '' } }, { hostProps: { condition: '' } },
); );
directive = spectator.directive; directive = spectator.directive;
cdr = (directive as any).cdRef as ChangeDetectorRef;
}); });
it('should be created', () => { it('should be created', () => {
@ -55,8 +58,22 @@ describe('PermissionDirective', () => {
grantedPolicy$.next(false); grantedPolicy$.next(false);
expect(spectator.query('#test-element')).toBeFalsy(); expect(spectator.query('#test-element')).toBeFalsy();
grantedPolicy$.next(true); grantedPolicy$.next(true);
expect(spectator.queryAll('#test-element')).toHaveLength(1);
});
it('should call detect changes method', () => {
const detectChanges = jest.spyOn(cdr, 'detectChanges');
expect(spectator.query('#test-element')).toBeFalsy();
spectator.setHostInput({ condition: 'test' });
grantedPolicy$.next(true);
expect(spectator.query('#test-element')).toBeTruthy();
expect(detectChanges).toHaveBeenCalled();
grantedPolicy$.next(false);
expect(spectator.query('#test-element')).toBeFalsy();
expect(detectChanges).toHaveBeenCalled();
grantedPolicy$.next(true); grantedPolicy$.next(true);
expect(spectator.queryAll('#test-element')).toHaveLength(1); expect(spectator.queryAll('#test-element')).toHaveLength(1);
expect(detectChanges).toHaveBeenCalled();
}); });
describe('#subscription', () => { describe('#subscription', () => {
@ -70,4 +87,35 @@ describe('PermissionDirective', () => {
}); });
}); });
}); });
describe('with runChangeDetection Input', () => {
beforeEach(() => {
spectator = createDirective(
'<div id="test-element" *abpPermission="condition;runChangeDetection:false">Testing Permission Directive</div>',
{ hostProps: { condition: '' } },
);
directive = spectator.directive;
cdr = (directive as any).cdRef as ChangeDetectorRef;
});
it('should not call detectChanges method', () => {
const detectChanges = jest.spyOn(cdr, 'detectChanges');
const markForCheck = jest.spyOn(cdr, 'markForCheck');
expect(spectator.query('#test-element')).toBeFalsy();
spectator.setHostInput({ condition: 'test' });
grantedPolicy$.next(true);
expect(spectator.query('#test-element')).toBeTruthy();
expect(detectChanges).not.toHaveBeenCalled();
expect(markForCheck).toHaveBeenCalled();
grantedPolicy$.next(false);
expect(spectator.query('#test-element')).toBeFalsy();
expect(detectChanges).not.toHaveBeenCalled();
expect(markForCheck).toHaveBeenCalled();
grantedPolicy$.next(true);
expect(spectator.queryAll('#test-element')).toHaveLength(1);
expect(detectChanges).not.toHaveBeenCalled();
expect(markForCheck).toHaveBeenCalled();
});
});
}); });

6
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html

@ -1,4 +1,8 @@
<div class="mb-3 form-group" *abpPermission="prop.permission" [ngSwitch]="getComponent(prop)"> <div
class="mb-3 form-group"
*abpPermission="prop.permission; runChangeDetection: false"
[ngSwitch]="getComponent(prop)"
>
<ng-template ngSwitchCase="input"> <ng-template ngSwitchCase="input">
<ng-template [ngTemplateOutlet]="label"></ng-template> <ng-template [ngTemplateOutlet]="label"></ng-template>
<input <input

2
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html

@ -24,7 +24,7 @@
[sortable]="prop.sortable" [sortable]="prop.sortable"
> >
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template> <ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container *abpPermission="prop.permission"> <ng-container *abpPermission="prop.permission; runChangeDetection: false">
<ng-container *ngIf="row['_' + prop.name]?.visible"> <ng-container *ngIf="row['_' + prop.name]?.visible">
<div <div
*ngIf="!row['_' + prop.name].component; else component" *ngIf="!row['_' + prop.name].component; else component"

4
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html

@ -27,7 +27,7 @@
<ng-container *ngIf="action.visible(data)"> <ng-container *ngIf="action.visible(data)">
<button <button
ngbDropdownItem ngbDropdownItem
*abpPermission="action.permission" *abpPermission="action.permission; runChangeDetection: false"
(click)="action.action(data)" (click)="action.action(data)"
type="button" type="button"
> >
@ -49,7 +49,7 @@
<ng-template #btnTmp let-action> <ng-template #btnTmp let-action>
<ng-container *ngIf="action.visible(data)"> <ng-container *ngIf="action.visible(data)">
<button <button
*abpPermission="action.permission" *abpPermission="action.permission; runChangeDetection: false"
(click)="action.action(data)" (click)="action.action(data)"
type="button" type="button"
class="btn btn-primary text-center" class="btn btn-primary text-center"

8
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html

@ -5,7 +5,7 @@
*ngFor="let action of actionList; trackBy: trackByFn; let last = last" *ngFor="let action of actionList; trackBy: trackByFn; let last = last"
> >
<ng-container *ngIf="action.visible(data)"> <ng-container *ngIf="action.visible(data)">
<ng-container *abpPermission="action.permission"> <ng-container *abpPermission="action.permission;runChangeDetection: false">
<ng-container *ngIf="action.component as component; else button"> <ng-container *ngIf="action.component as component; else button">
<ng-container <ng-container
*ngComponentOutlet="component; injector: record | createInjector: action:this" *ngComponentOutlet="component; injector: record | createInjector: action:this"
@ -13,7 +13,11 @@
</ng-container> </ng-container>
<ng-template #button> <ng-template #button>
<button (click)="action.action(data)" type="button" [ngClass]="action.btnClass ? action.btnClass : defaultBtnClass" > <button
(click)="action.action(data)"
type="button"
[ngClass]="action.btnClass ? action.btnClass : defaultBtnClass"
>
<i [ngClass]="action.icon" [class.me-1]="action.icon"></i> <i [ngClass]="action.icon" [class.me-1]="action.icon"></i>
{{ action.text | abpLocalization }} {{ action.text | abpLocalization }}
</button> </button>

4
npm/ng-packs/packages/theme-shared/src/lib/constants/validation.ts

@ -13,4 +13,8 @@ export const DEFAULT_VALIDATION_BLUEPRINTS = {
range: 'AbpValidation::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]', range: 'AbpValidation::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]',
required: 'AbpValidation::ThisFieldIsRequired.', required: 'AbpValidation::ThisFieldIsRequired.',
url: 'AbpValidation::ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl', url: 'AbpValidation::ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl',
passwordRequiresLower: 'AbpIdentity::Volo.Abp.Identity:PasswordRequiresLower',
passwordRequiresUpper: 'AbpIdentity::Volo.Abp.Identity:PasswordRequiresUpper',
passwordRequiresDigit: 'AbpIdentity::Volo.Abp.Identity:PasswordRequiresDigit',
passwordRequiresNonAlphanumeric: 'AbpIdentity::Volo.Abp.Identity:PasswordRequiresNonAlphanumeric',
}; };

1
npm/ng-packs/packages/theme-shared/src/lib/models/index.ts

@ -4,3 +4,4 @@ export * from './nav-item';
export * from './statistics'; export * from './statistics';
export * from './toaster'; export * from './toaster';
export * from './user-menu'; export * from './user-menu';
export * from './validation';

1
npm/ng-packs/packages/theme-shared/src/lib/models/validation.ts

@ -0,0 +1 @@
export type PasswordRule = 'small' | 'capital' | 'number' | 'special';

10
npm/ng-packs/packages/theme-shared/src/lib/tests/validation-utils.spec.ts

@ -4,10 +4,11 @@ import { HttpClient } from '@angular/common/http';
import { Component, Injector } from '@angular/core'; import { Component, Injector } from '@angular/core';
import { Validators } from '@angular/forms'; import { Validators } from '@angular/forms';
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { validatePassword } from '@ngx-validate/core';
import { OAuthService } from 'angular-oauth2-oidc'; import { OAuthService } from 'angular-oauth2-oidc';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { getPasswordValidators } from '../utils'; import { getPasswordValidators, validatePassword } from '../utils';
import { PasswordRule } from '../models/validation';
@Component({ template: '', selector: 'abp-dummy' }) @Component({ template: '', selector: 'abp-dummy' })
class DummyComponent {} class DummyComponent {}
@ -47,8 +48,11 @@ describe('ValidationUtils', () => {
configState.refreshAppState(); configState.refreshAppState();
const validators = getPasswordValidators(spectator.inject(Injector)); const validators = getPasswordValidators(spectator.inject(Injector));
const passwordValidators = ['number', 'small', 'capital', 'special'].map(
(rule: PasswordRule) => validatePassword(rule),
);
const expectedValidators = [ const expectedValidators = [
validatePassword(['number', 'small', 'capital', 'special']), ...passwordValidators,
Validators.minLength(6), Validators.minLength(6),
Validators.maxLength(128), Validators.maxLength(128),
]; ];

44
npm/ng-packs/packages/theme-shared/src/lib/utils/validation-utils.ts

@ -1,7 +1,8 @@
import { ABP, ConfigStateService } from '@abp/ng.core'; import { ABP, ConfigStateService } from '@abp/ng.core';
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { ValidatorFn, Validators } from '@angular/forms'; import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
import { PasswordRules, validatePassword } from '@ngx-validate/core'; import { normalizeDiacritics, PasswordRules } from '@ngx-validate/core';
import { PasswordRule } from "../models/validation";
const { minLength, maxLength } = Validators; const { minLength, maxLength } = Validators;
@ -31,7 +32,8 @@ export function getPasswordValidators(injector: Injector): ValidatorFn[] {
requiredLength = +getRule('RequiredLength'); requiredLength = +getRule('RequiredLength');
} }
return [validatePassword(passwordRulesArr), minLength(requiredLength), maxLength(128)]; const passwordValidators = passwordRulesArr.map(rule => validatePassword(rule));
return [...passwordValidators, minLength(requiredLength), maxLength(128)];
} }
function getRuleFn(injector: Injector) { function getRuleFn(injector: Injector) {
@ -43,3 +45,39 @@ function getRuleFn(injector: Injector) {
return (passwordRules[`Abp.Identity.Password.${key}`] || '').toLowerCase(); return (passwordRules[`Abp.Identity.Password.${key}`] || '').toLowerCase();
}; };
} }
const errorMessageMap = {
small: 'passwordRequiresLower',
capital: 'passwordRequiresUpper',
number: 'passwordRequiresDigit',
special: 'passwordRequiresNonAlphanumeric',
};
export function validatePassword(
shouldContain: PasswordRule
): ValidatorFn {
return (control: AbstractControl) => {
if (!control.value) return null;
const value = normalizeDiacritics(control.value);
const regexMap = {
small: /.*[a-z].*/,
capital: /.*[A-Z].*/,
number: /.*[0-9].*/,
special: /.*[^0-9a-zA-Z].*/,
};
const regex = regexMap[shouldContain];
const isValid = regex.test(value);
if (isValid) {
return null;
}
const error = errorMessageMap[shouldContain];
return {
[error]: true,
};
};
}

3
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs

@ -1,5 +1,6 @@
using Blazorise.Bootstrap5; using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome; using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
@ -137,7 +138,7 @@ public class MyProjectNameModule : AbpModule
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
} }
ConfigureUrls(configuration); ConfigureUrls(configuration);
ConfigureBundles(); ConfigureBundles();
ConfigureAutoMapper(context); ConfigureAutoMapper(context);

3
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs

@ -1,5 +1,6 @@
using Blazorise.Bootstrap5; using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome; using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
@ -133,7 +134,7 @@ public class MyProjectNameModule : AbpModule
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment()) if (hostingEnvironment.IsDevelopment())
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());

3
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
@ -120,7 +121,7 @@ public class MyProjectNameModule : AbpModule
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment()) if (hostingEnvironment.IsDevelopment())
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());

3
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
@ -122,7 +123,7 @@ public class MyProjectNameModule : AbpModule
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment()) if (hostingEnvironment.IsDevelopment())
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());

5
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyProjectNameModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
using MyCompanyName.MyProjectName.Localization; using MyCompanyName.MyProjectName.Localization;
@ -126,7 +127,7 @@ public class MyProjectNameModule : AbpModule
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment()) if (hostingEnvironment.IsDevelopment())
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());

5
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyProjectNameModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Data; using MyCompanyName.MyProjectName.Data;
using MyCompanyName.MyProjectName.Localization; using MyCompanyName.MyProjectName.Localization;
@ -127,7 +128,7 @@ public class MyProjectNameModule : AbpModule
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment()) if (hostingEnvironment.IsDevelopment())
{ {
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs

@ -2,6 +2,7 @@ using System.IO;
using Blazorise.Bootstrap5; using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome; using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

1
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Blazorise.Bootstrap5; using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome; using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;

Loading…
Cancel
Save