Browse Source

Merge branch 'dev' into auto-merge/rel-6-0/1333

pull/13929/head
Enis Necipoglu 4 years ago
committed by GitHub
parent
commit
c77cdbc117
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/workflows/labeler.yml
  2. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ro-RO.json
  4. 17
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  5. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json
  6. 17
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  7. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hant.json
  8. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json
  9. 10
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  10. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json
  11. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  12. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  13. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  14. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/AbpIoWwwResource.cs
  15. 24
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  16. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ro-RO.json
  17. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/tr.json
  18. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  19. 1
      common.DotSettings
  20. 2
      common.props
  21. 35
      docs/en/Background-Jobs-Hangfire.md
  22. 12
      docs/en/Background-Jobs-RabbitMq.md
  23. 690
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/How-to-Design-Multi-Lingual-Entity.md
  24. BIN
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/data-model.png
  25. BIN
      docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/result.gif
  26. 4
      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
  27. 154
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
  28. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png
  29. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png
  30. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png
  31. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png
  32. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png
  33. BIN
      docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png
  34. 34
      docs/en/Dependency-Injection.md
  35. 452
      docs/en/Deploy-azure-app-service.md
  36. 3
      docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
  37. 15
      docs/en/Entities.md
  38. 1
      docs/en/Multi-Tenancy.md
  39. 155
      docs/en/Themes/LeptonXLite/AspNetCore.md
  40. BIN
      docs/en/images/leptonxlite-brand-component.png
  41. BIN
      docs/en/images/leptonxlite-breadcrumb-component.png
  42. BIN
      docs/en/images/leptonxlite-language-switch-component.png
  43. BIN
      docs/en/images/leptonxlite-mobile-language-switch-component.png
  44. BIN
      docs/en/images/leptonxlite-mobile-user-menu-component.png
  45. BIN
      docs/en/images/leptonxlite-page-alerts-component.png
  46. BIN
      docs/en/images/leptonxlite-sidebar-menu-component.png
  47. BIN
      docs/en/images/leptonxlite-user-menu-component.png
  48. 140
      docs/es/Getting-Started-AspNetCore-Application.md
  49. BIN
      docs/es/images/create-aspnet-core-application.png
  50. BIN
      docs/es/images/create-new-aspnet-core-application-v2.png
  51. BIN
      docs/es/images/select-empty-web-application-v2.png
  52. 183
      docs/pt-BR/Validation.md
  53. 35
      docs/zh-Hans/Background-Jobs-Hangfire.md
  54. 10
      docs/zh-Hans/Background-Jobs-RabbitMq.md
  55. 3
      docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
  56. 16
      docs/zh-Hans/Entities.md
  57. 1
      docs/zh-Hans/Multi-Tenancy.md
  58. 42
      framework/Volo.Abp.sln
  59. 7
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor
  60. 39
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor.cs
  61. 6
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/StandardLayouts.cs
  62. 5
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs
  63. 1
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs
  64. 3
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs
  65. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xml
  66. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xsd
  67. 18
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj
  68. 29
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs
  69. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusOptions.cs
  70. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs
  71. 63
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProvider.cs
  72. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProviderContributorContext.cs
  73. 36
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs
  74. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/IAbpAspNetCoreMvcDaprPubSubProviderContributor.cs
  75. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionDefinition.cs
  76. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs
  77. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy.cs
  78. 25
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs
  79. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xml
  80. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xsd
  81. 22
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj
  82. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/AbpAspNetCoreMvcDaprModule.cs
  83. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSize.cs
  84. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSizeExtensions.cs
  85. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarManager.cs
  86. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/Default.cshtml
  87. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookViewComponent.cs
  88. 7
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  89. 2
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Hosting/AbpHostingEnvironmentExtensions.cs
  90. 52
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/DependencyInjection/HttpContextServiceScopeFactory.cs
  91. 4
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs
  92. 2
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs
  93. 2
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs
  94. 4
      framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs
  95. 17
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  96. 16
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
  97. 14
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs
  98. 3
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
  99. 13
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs
  100. 14
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

4
.github/workflows/labeler.yml

@ -2,8 +2,12 @@ name: Pull request labeler
on:
schedule:
- cron: '0 12 */1 * *'
permissions:
contents: read
jobs:
labeler:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: paulfantom/periodic-labeler@master

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

@ -397,6 +397,8 @@
"BookDiscountDeletionConfirmationMessage": "Are you sure you want to delete this book discount?",
"CustomPaymentFlexSwitchDescription": "With license",
"AllowFeatureUpgradeOnLicenseExpire": "Allow feature upgrade on license expire",
"Deleted{0}": "[Deleted {0}]"
"Deleted{0}": "[Deleted {0}]",
"Tags": "Tags",
"SetTagsInfo": "Tags should be comma-separated. Eg: CSharp, Entity Framework"
}
}

6
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/ro-RO.json

@ -30,7 +30,7 @@
"NugetPackageDeletionWarningMessage": "Sunteţi sigur(ă) că doriţi să ştergeţi acest pachet de tip Nuget?",
"ModuleDeletionWarningMessage": "Sunteţi sigur(ă) că doriţi să ştergeţi acest modul?",
"Name": "Nume",
"DisplayName": "Nume de afişare",
"DisplayName": "Nume afişat",
"ShortDescription": "Descriere scurtă",
"NameFilter": "Nume",
"CreationTime": "Data şi ora creării",
@ -317,7 +317,7 @@
"TrialLicenseStartDateFilter": "Data de început",
"TrialLicenseEndDateFilter": "Data de încheiere",
"FirsName": "Nume",
"LastName": "Nume",
"LastName": "Nume de familie",
"StartDate": "Data de început",
"EndDate": "Data de încheiere",
"PurchasedDate": "Data achiziției",
@ -350,4 +350,4 @@
"Volo.AbpIo.Commercial:030010": "Pentru a achiziționa licența de probă, mai întâi trebuie să vă activați licența de probă!",
"Volo.AbpIo.Commercial:030011": "Nu puteți șterge o licență de probă atunci când este achiziționată!"
}
}
}

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

@ -73,6 +73,7 @@
"DeveloperFocused": "Developer Focused",
"ShareYourExperiences": "Share your experiences with the ABP Framework",
"LatestPosts": "Latest Posts",
"LatestVideos": "Latest Videos",
"Views": "Views",
"LearnLatestNewsAboutABPFramework": "Get information about happenings in ABP like new releases, free sources, posts, and more.",
"DeveloperTools": "Developer Tools",
@ -98,6 +99,7 @@
"Logout": "Logout",
"Home": "Home",
"Posts": "Posts",
"Videos": "Videos",
"JoinTheABPCommunity": "Join the ABP Community",
"SubmitYourPost": "Submit Your Post",
"Modules": "Modules",
@ -164,6 +166,19 @@
"Books": "Books",
"ABPDiscordServer": "ABP Discord Server",
"ABPCommunityTalks": "ABP Community Talks",
"ABPCommunityPosts": "ABP Community Posts"
"ABPCommunityPosts": "ABP Community Posts",
"BuyAndGetMonths": "BUY 12 MONTHS, <span class=\"text-info\">GET 14 MONTHS!</span>",
"GetYourDeal": "Get Your Deal",
"BuyOrRenewLicense": "Buy or Renew License Now and Get 2 Extra Months!",
"BuyOrRenewLicenseToGetExtra2Months": "Buy or Renew License Now and Get 2 Extra Months! HURRY UP! ⏰ Last Day: {0}",
"HurryUp": "HURRY UP!",
"LastDay": "Last Day: {0}",
"BuyNewLicenseBetweenDatesToGetBenefit": "Buy a new license between {0} and {1} to get benefit for extra 2 months!",
"CheckAllCommunityTalks": "Check All Community Posts",
"ReadMore": "Read More",
"Post": "Post",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community.",
"WelcomeFallCampaign": "Welcome Fall Campaign!",
"GiveAwayForNewPurchases": "Application Development Classroom Training will be given away for the new purchases!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "Deneme lisansınızın süresi {0} gün önce sona erdi.",
"TrialLicensePeriodWillExpire": "Deneme lisansınızın süresi {0} gün içinde dolacak.",
"TrialLicensePeriodExpireToday": "Deneme lisans süreniz bugün sona erecek.",
"PurchaseNow": "Şimdi satın al!"
"PurchaseNow": "Şimdi satın al!",
"WelcomeFallCampaign": "Hoş Geldin Sonbahar Kampanyası!",
"GiveAwayForNewPurchases": "Yeni alımlar için Uygulama Geliştirme Sınıfı Eğitimi hediye edilecektir!"
}
}

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

@ -73,6 +73,7 @@
"DeveloperFocused": "以开发者为中心",
"ShareYourExperiences": "分享您使用 ABP 框架的经验",
"LatestPosts": "最新的帖子",
"LatestVideos": "最新的视频",
"Views": "意见",
"LearnLatestNewsAboutABPFramework": "获取有关 ABP 的最新相关信息,例如新版本、免费资源、帖子等。",
"DeveloperTools": "开发者工具",
@ -98,6 +99,7 @@
"Logout": "登出",
"Home": "主页",
"Posts": "帖子",
"Videos": "视频",
"JoinTheABPCommunity": "加入 ABP 社区",
"SubmitYourPost": "提交您的帖子",
"Modules": "模块",
@ -161,6 +163,19 @@
"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": "书籍"
"Books": "书籍",
"BuyAndGetMonths": "购买 12 个月,<span class=\"text-info\">获得 14 个月!</span>",
"GetYourDeal": "得到你的交易",
"BuyOrRenewLicense": "立即购买或续订许可证并额外获得 2 个月!",
"BuyOrRenewLicenseToGetExtra2Months": "立即购买或续订 ABP 商业许可证(适用于所有版本)并额外获得 2 个月!",
"HurryUp": "赶快下单!",
"LastDay": "活动截止日期: {0}",
"BuyNewLicenseBetweenDatesToGetBenefit": "在 {0} 和 {1} 之间购买一个新的许可证以获得额外 2 个月的收益!",
"CheckAllCommunityTalks": "检查所有社区帖子",
"ReadMore": "阅读更多",
"Post": "邮政",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hant.json

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "您的試用許可期限已於 {0} 天前到期。",
"TrialLicensePeriodWillExpire": "您的試用許可期限將在 {0} 天后到期。",
"TrialLicensePeriodExpireToday": "您的試用許可期將於今天到期。",
"PurchaseNow": "現在買!"
"PurchaseNow": "現在買!",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json

@ -89,7 +89,7 @@
"Blogging": "التدوين",
"Identity": "هوية",
"IdentityServer": "خادم الهوية",
"Saas": "ساس",
"Saas": "البرمجيات كخدمة",
"LanguageManagement": "إدارة اللغة",
"TextTemplateManagement": "إدارة قالب النص",
"See All Modules": "انظر جميع الوحدات",
@ -382,4 +382,4 @@
"RenewLicenseEarly": "إذا قمت بتجديد رخصتي في وقت مبكر ، هل سأحصل على السنة كاملة؟",
"RenewLicenseEarylExplanation": "عند تجديد الترخيص الخاص بك قبل تاريخ انتهاء الترخيص الخاص بك ، ستتم إضافة سنة واحدة إلى تاريخ انتهاء الترخيص الخاص بك. على سبيل المثال ، إذا انتهت صلاحية ترخيصك في {0} -06-06 وقمت بتجديده في {0} -01-01 ، فسيكون تاريخ انتهاء صلاحية الترخيص الجديد {1} -06-06."
}
}
}

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

@ -735,6 +735,14 @@
"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?"
"WhichLicenseTypeYouAreInterestedIn": "Which license type you are interested in?",
"DontTakeOurWordForIt": "Don't take our word for it...",
"ReadAbpCommercialUsersWantYouToKnow": "Read what ABP Commercial users want you to know",
"Testimonial_ShortDescription_1": "The modularity of ABP made it possible for the team to deliver in time.",
"Testimonial_ShortDescription_2": "Build new features faster than before.",
"Testimonial_ShortDescription_3": "We start from out-of-the-box features and just focus on what we really need to write.",
"Testimonial_ShortDescription_4": "ABP Commercial was the best fit for our needs.",
"OnlineReviewersOnAbpCommercial": "Online Reviews on ABP Commercial",
"SeeWhatToldAboutAbpCommercial": "See what has been told about ABP Commercial and write your thoughts if you want."
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json

@ -341,7 +341,7 @@
"SignIn": "Autentificare",
"Or": "Sau",
"TellUsAboutYourself": "Spuneţi-ne despre dumneavoastră",
"Surname": "Nume",
"Surname": "Nume de familie",
"DoYouAgreePrivacyPolicy": "Sunt de acord cu <a href=\"/TermsConditions\">Termenii şi condiţiile<a/> şi <a href=\"/Privacy\">Politica de confidenţialitate</a>.",
"VolosoftMarketingInformationMessage": "Sunt de acord să primesc informaţii, sfaturi şi oferte despre soluţii pentru afaceri şi organizaţii şi alte produse şi servicii Volosoft.",
"VolosoftSharingInformationMessage": "Sunt de acord ca Volosoft să partajeze informaţiile mele cu partenerii selectaţi astfel încât să primesc informaţii relevante despre produsele şi serviciile lor.",
@ -380,4 +380,4 @@
"TrialLicenseExpiredInfo": "Perioada de licență de probă a expirat!",
"CommercialNewsletterConfirmationMessage": "Sunt de acord cu <a href=\"https://commercial.abp.io/TermsConditions\">Termenii și condițiile</a> și cu <a href=\"https://commercial.abp.io/Privacy\">Politica de confidențialitate </a>."
}
}
}

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

@ -565,7 +565,7 @@
"PrePayment_IyzicoRedirectionInfo": "您将被重定向到 Iyzico 支付网关以安全地完成您的购买。",
"PrePayment_IyzicoAcceptVisaAndMasterCard": "Iyzico 接受 Visa 和 MasterCard。",
"Purchase": "购买",
"AcceptTermsAndConditions": "我已阅读、理解并接受<a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">隐私政策</a>, <a href=\"{1}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">条款和条件</a>和<a href=\"{ 2}\" target=\"_blank\" class=\"text-primary\"> EULA。</a>",
"AcceptTermsAndConditions": "我已阅读、理解并接受<a href=\"{0}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">隐私政策</a>, <a href=\"{1}\" target=\"_blank\" class=\"text-primary\" rel=\"noopener\">条款和条件</a>和<a href=\"{2}\" target=\"_blank\" class=\"text-primary\"> EULA。</a>",
"AcceptTermsAndConditionsWarningMessage": "请接受隐私政策和条款和条件",
"SelectGatewayToContinue": "请选择一个网关以继续!",
"GatewaySelection_SelectGateway": "选择支付网关",

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

@ -150,7 +150,6 @@
"GetStarted": "Get Started",
"SourceCode": "Source Code",
"LeaveComment": "Leave Comment",
"ReadMore": "Read more",
"ShowMore": "Show More",
"NoPublishedPostsYet": "No published posts yet.",
"Name": "Name",
@ -184,6 +183,7 @@
"Post_Index_Page_MetaDescription": "ABP Community's purpose is to create a contribution environment for developers who use the ABP framework.",
"Layout_Title": "{0} | ABP Community",
"Layout_MetaDescription": "ABP Community is an environment where people can share posts about ABP framework and follows the projects.",
"Index_Page_CommunityIntroduction": "This is a hub for ABP Framework, .NET and software development. You can read the articles, watch the video tutorials, get informed about ABP’s development progress and ABP-related events, help other developers and share your expertise with the ABP community."
"Index_Page_CommunityIntroduction": "This is a hub for ABP Framework, .NET and software development. You can read the articles, watch the video tutorials, get informed about ABP’s development progress and ABP-related events, help other developers and share your expertise with the ABP community.",
"TagsInArticle": "Tags in article"
}
}

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

@ -150,7 +150,6 @@
"GetStarted": "开始使用",
"SourceCode": "源代码",
"LeaveComment": "发表评论",
"ReadMore": "阅读更多",
"ShowMore": "展示更多",
"NoPublishedPostsYet": "还没有发布的帖子。",
"Name": "名字",

5
abp_io/AbpIoLocalization/AbpIoLocalization/Www/AbpIoWwwResource.cs

@ -1,5 +1,8 @@
namespace AbpIoLocalization.Www
using Volo.Abp.Localization;
namespace AbpIoLocalization.Www
{
[LocalizationResourceName("AbpIoWww")]
public class AbpIoWwwResource
{

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

@ -293,7 +293,6 @@
"ExploreDocumentationAndGuides": "Explore the comprehensive documentation and guides.",
"Documentations": "Documentation",
"Views": "Views",
"ReadMore": "Read More",
"EnterYouEmailToGetNews": "Enter your email to get the latest news about the ABP Framework",
"Tiered": "Tiered",
"SeparateIdentityServer": "Separate Identity Server",
@ -372,6 +371,27 @@
"MasteringAbpFramework_Book_What_You_Will_Learn_8": "Write unit, integration, and UI tests using ABP Framework.",
"MasteringAbpFramework_Book_WhoIsThisBookFor": "Who's this book for",
"MasteringAbpFramework_Book_WhoIsThisBookFor_Description": "This book is for web developers who want to learn software architectures and best practices for building\n maintainable web-based solutions using Microsoft technologies and ABP Framework. Basic knowledge of C#\n and ASP.NET Core is necessary to get started with this book.",
"ComputersAndTechnology": "Computers & Technology"
"ComputersAndTechnology": "Computers & Technology",
"BuildingMicroserviceSolutions": "Building Microservice Solutions",
"MicroserviceBookPracticalGuide": "This book is a reference guide for developing and managing microservice-based applications using the ABP Framework. It references the <strong>.NET Microservice Sample Reference Application</strong>: eShopOnContainers and discusses the architectural design and implementation approaches using the ABP Framework. By the end of this book, you'll learn how ABP approaches the common microservice complexities such as authorization, distributed transactions, inter-microservice communications, deployment, etc.",
"IntroducingTheSolution": "Introducing the eShopOnAbp Solution",
"RunningTheSolution": "Running the Solution",
"UnderstandingTheAuthenticationSystem": "Understanding the Authentication System",
"ExploringTheApplications": "Exploring the Applications",
"UnderstandingTheAPIGateways": "Understanding the API Gateways",
"DevelopingTheMicroservices": "Developing the Microservices",
"UnderstandingTheInfrastructure": "Understanding the Infrastructure",
"DiggingInTheUseCases": "Digging in the Use Cases",
"DeployingTheSolution": "Deploying the Solution",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "This book is in draft stage and is not completed yet.",
"Authors": "Authors",
"MicroserviceEBook": "Microservice E-Book",
"SelectUITheme": "Select UI Theme",
"LeptonXLiteTheme": "LeptonX Lite Theme",
"BasicTheme": "Basic Theme",
"LeptonXLiteThemeInfo": " A modern and stylish Bootstrap UI theme. Ideal if you want to have a production ready UI theme. This is the newest theme and is the default.",
"BasicThemeInfo": "Minimalist UI theme with plain Bootstrap colors and styles. Ideal if you will build your own UI theme.",
"SeeDocumentation": "See <a href='{0}' target='_blank'>documentation</a>.",
"SeeFullScreen": "<a href='{0}' target='_blank'>🖼️ See the screenshot</a>"
}
}

6
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/ro-RO.json

@ -253,18 +253,18 @@
"Or": "Sau",
"TellUsAboutYourself": "Spuneţi-ne un pic despre dumneavoastră",
"Name": "Nume",
"Surname": "Nume",
"Surname": "Nume de familie",
"CompanyName": "Nume companie",
"DoYouAgreePrivacyPolicy": "Sunt de acord cu <a href=\"https://account.abp.io/Account/TermsConditions\">Termenii & condiţiile</a> şi <a href=\"https://account.abp.io/Account/Privacy\">Politica de confidenţialitate</a>.",
"Free": "Gratuit",
"DDDEBook": "E-book DDD",
"PracticalGuideForImplementingDDD": "Această carte este un ghid practic pentru implementarea Domain Driven Design în framework-ul ABP.",
"IntroducingDDD": "Introducere în Domain Driven Design",
"DDDLayersAndCleanArchitecture": "Straturile DDD & Arhitectură curată",
"DDDLayersAndCleanArchitecture": "Straturile DDD şi Arhitectură curată",
"LayeringOfADotnetSolution": "Stratificarea unei soluţii .NET",
"ImplementingDDDBuildingBlocks": "Implementând DDD Building Blocks",
"DomainVsApplicationLogic": "Domain Logic vs Application Logic",
"SamplesAndDiscussions": "Exemple & Discuţii",
"SamplesAndDiscussions": "Exemple şi Discuţii",
"EmailNotValid": "Vă rugăm să introduceţi o adresa de email validă.",
"WeWillSendYouADownloadLink": "Un link care conţine e-book-ul a fost trimis către {0}. Verificaţi-vă folderele de inbox, junk sau spam!",
"GoHome": "Pagina principală",

3
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/tr.json

@ -279,6 +279,7 @@
"SubscribeToNewsletter": "ABP.IO Platform'u ile ilgili yeni haberler, makaleler, teklifler ve daha fazlası gibi gelişmeler hakkında bilgi almak için bültene abone olun.",
"FirstEdition": "İlk Baskı",
"ThankYou": "Teşekkürler!",
"CheckboxMandatory": "Devam etmek için bunu kontrol etmeniz gerekiyor!"
"CheckboxMandatory": "Devam etmek için bunu kontrol etmeniz gerekiyor!",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "Bu kitap taslak aşamasındadır ve henüz tamamlanmamıştır."
}
}

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

@ -293,7 +293,6 @@
"ExploreDocumentationAndGuides": "探索全面的文档和指南。",
"Documentations": "文档",
"Views": "意见",
"ReadMore": "阅读更多",
"EnterYouEmailToGetNews": "输入您的电子邮件以获取有关 ABP 框架的最新消息",
"Tiered": "分层",
"SeparateIdentityServer": "独立的身份服务器",
@ -372,6 +371,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": "计算机与技术"
"ComputersAndTechnology": "计算机与技术",
"ThisBookIsInDraftStageAndIsNotCompletedYet": "这本书正在草案阶段,还没有完成。"
}
}

1
common.DotSettings

@ -38,5 +38,6 @@
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dapr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>6.0.0-rc.4</Version>
<Version>7.0.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>

35
docs/en/Background-Jobs-Hangfire.md

@ -80,6 +80,41 @@ After you have installed these NuGet packages, you need to configure your projec
}
````
### Specifying Queue
You can use the [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) to specify the queue.
````csharp
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
namespace MyProject
{
[Queue("alpha")]
public class EmailSendingJob
: AsyncBackgroundJob<EmailSendingArgs>, ITransientDependency
{
private readonly IEmailSender _emailSender;
public EmailSendingJob(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public override async Task ExecuteAsync(EmailSendingArgs args)
{
await _emailSender.SendAsync(
args.EmailAddress,
args.Subject,
args.Body
);
}
}
}
````
### Dashboard Authorization
Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard.

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

@ -126,25 +126,31 @@ By default, all the job types use the `Default` RabbitMQ connection.
Configure<AbpRabbitMqBackgroundJobOptions>(options =>
{
options.DefaultQueueNamePrefix = "my_app_jobs.";
options.DefaultDelayedQueueNamePrefix = "my_app_jobs.delayed"
options.PrefetchCount = 1;
options.JobQueues[typeof(EmailSendingArgs)] =
new JobQueueConfiguration(
typeof(EmailSendingArgs),
queueName: "my_app_jobs.emails",
connectionName: "SecondConnection"
connectionName: "SecondConnection",
delayedQueueName:"my_app_jobs.emails.delayed"
);
});
````
* This example sets the default queue name prefix to `my_app_jobs.`. If different applications use the same RabbitMQ server, it would be important to use different prefixes for each application to not consume jobs of each other.
* This example sets the default queue name prefix to `my_app_jobs.` and default delayed queue name prefix to `my_app_jobs.delayed`. If different applications use the same RabbitMQ server, it would be important to use different prefixes for each application to not consume jobs of each other.
* Sets `PrefetchCount` for all queues.
* Also specifies a different connection string for the `EmailSendingArgs`.
`JobQueueConfiguration` class has some additional options in its constructor;
* `queueName`: The queue name that is used for this job. The prefix is not added, so you need to specify the full name of the queue.
* `DelayedQueueName`: The delayed queue name that is used for delayed execution of job. The prefix is not added, so you need to specify the full name of the queue.
* `connectionName`: The RabbitMQ connection name (see the connection configuration above). This is optional and the default value is `Default`.
* `durable` (optional, default: `true`).
* `exclusive` (optional, default: `false`).
* `autoDelete` (optional, default: `false`)
* `autoDelete` (optional, default: `false`).
* `PrefetchCount` (optional, default: null)
See the RabbitMQ documentation if you want to understand the `durable`, `exclusive` and `autoDelete` options better, while most of the times the default configuration is what you want.

690
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/How-to-Design-Multi-Lingual-Entity.md

@ -0,0 +1,690 @@
# How to Design Multi-Lingual Entity
## Introduction
If you want to open up to the global market these days, end-to-end localization is a must. ABP provides an already established infrastructure for static texts. However, this may not be sufficient for many applications. You may need to fully customize your app for a particular language and region.
Let's take a look at a few quotes from Christian Arno's article "[How Foreign-Language Internet Strategies Boost Sales](https://www.mediapost.com/publications/article/155250/how-foreign-language-internet-strategies-boost-sal.html)" to better understand the impact of this:
- 82% of European consumers are less likely to buy online if the site is not in their native tongue ([Eurobarometer survey](http://europa.eu/rapid/pressReleasesAction.do?reference=IP/11/556)).
- 72.4% of global consumers are more likely to buy a product if the information is available in their own language ([Common Sense Advisory](http://www.commonsenseadvisory.com/)).
- The English language currently only accounts for 31% of all online use, and more than half of all searches are in languages other than English.
- Today, 42% of all Internet users are in Asia, while almost one-quarter are in Europe and just over 10% are in Latin America.
- Foreign languages have experienced exponential growth in online usage in the past decade -- with Chinese now officially the [second-most-prominent-language](http://english.peopledaily.com.cn/90001/90776/90882/7438489.html) on the Web. [Arabic](http://www.internetworldstats.com/stats7.htm) has increased by a whopping 2500%, while English has only risen by 204%
If you are looking for ways to expand your market share by fully customizing your application for a particular language and region, in this article I will explain how you can do it with ABP framework.
### Source Code
You can find the source code of the application at [abpframework/abp-samples](https://github.com/abpframework/abp-samples/tree/master/AcmeBookStoreMultiLingual).
### Demo of the Final Application
At the end of this article, we will have created an application same as in the gif below.
![data-model](./result.gif)
## Development
In order to keep the article short and get rid of unrelated information in the article (like defining entities etc.), we'll be using the [BookStore](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) example, which is used in the "[Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)" documentation of ABP Framework and we will make the Book entity as multi-lingual. If you do not want to finish this tutorial, you can find the application [here](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore).
### Determining the data model
We need a robust, maintainable, and efficient data model to store content in multiple languages.
> I read many articles to determine the data model correctly, and as a result, I decided to use one of the many approaches that suit us.
> However, as in everything, there is a trade-off here. If you are wondering about the advantages and disadvantages of the model we will implement compared to other models, I recommend you to read [this article](https://vertabelo.com/blog/data-modeling-for-multiple-languages-how-to-design-a-localization-ready-system/).
![data-model](./data-model.png)
As a result of the tutorial, we already have the `Book` and `Author` entities, as an extra, we will just add the `BookTranslation`.
> In the article, we will make the Name property of the Book entity multi-lingual, but the article is independent of the Book entity, you can make the entity you want multi-lingual with similar codes according to your requirements.
#### Acme.BookStore.Domain.Shared
Create a folder named `MultiLingualObjects` and create the following interfaces in its contents.
We will use the `IObjectTranslation` interface to mark the translation of a multi-lingual entity:
```csharp
public interface IObjectTranslation
{
string Language { get; set; }
}
```
We will use the `IMultiLingualObject<TTranslation>` interface to mark multi-lingual entities:
```csharp
public interface IMultiLingualObject<TTranslation>
where TTranslation : class, IObjectTranslation
{
ICollection<TTranslation> Translations { get; set; }
}
```
#### Acme.BookStore.Domain
In the `Books` folder, create the `BookTranslation` class as follows:
```csharp
public class BookTranslation : Entity, IObjectTranslation
{
public Guid BookId { get; set; }
public string Name { get; set; }
public string Language { get; set; }
public override object[] GetKeys()
{
return new object[] {BookId, Language};
}
}
```
`BookTranslation` contains the `Language` property, which contains a language code for translation and a reference to the multi-lingual entity. We also have the `BookId` foreign key to help us know which book is translated.
Implement `IMultiLingualObject` in the `Book` class as follows:
```csharp
public class Book : AuditedAggregateRoot<Guid>, IMultiLingualObject<BookTranslation>
{
public Guid AuthorId { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public ICollection<BookTranslation> Translations { get; set; }
}
```
Create a folder named `MultiLingualObjects` and add the following class inside of this folder:
```csharp
public class MultiLingualObjectManager : ITransientDependency
{
protected const int MaxCultureFallbackDepth = 5;
public async Task<TTranslation> FindTranslationAsync<TMultiLingual, TTranslation>(
TMultiLingual multiLingual,
string culture = null,
bool fallbackToParentCultures = true)
where TMultiLingual : IMultiLingualObject<TTranslation>
where TTranslation : class, IObjectTranslation
{
culture ??= CultureInfo.CurrentUICulture.Name;
if (multiLingual.Translations.IsNullOrEmpty())
{
return null;
}
var translation = multiLingual.Translations.FirstOrDefault(pt => pt.Language == culture);
if (translation != null)
{
return translation;
}
if (fallbackToParentCultures)
{
translation = GetTranslationBasedOnCulturalRecursive(
CultureInfo.CurrentUICulture.Parent,
multiLingual.Translations,
0
);
if (translation != null)
{
return translation;
}
}
return null;
}
protected TTranslation GetTranslationBasedOnCulturalRecursive<TTranslation>(
CultureInfo culture, ICollection<TTranslation> translations, int currentDepth)
where TTranslation : class, IObjectTranslation
{
if (culture == null ||
culture.Name.IsNullOrWhiteSpace() ||
translations.IsNullOrEmpty() ||
currentDepth > MaxCultureFallbackDepth)
{
return null;
}
var translation = translations.FirstOrDefault(pt => pt.Language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase));
return translation ?? GetTranslationBasedOnCulturalRecursive(culture.Parent, translations, currentDepth + 1);
}
}
```
With `MultiLingualObjectManager`'s `FindTranslationAsync` method, we get the translated version of the book according to `CurrentUICulture`. If no translation of culture is found, we return null.
> Every thread in .NET has `CurrentCulture` and `CurrentUICulture` objects.
#### Acme.BookStore.EntityFrameworkCore
In the `OnModelCreating` method of the `BookStoreDbContext` class, configure the `BookTranslation` as follows:
```csharp
builder.Entity<BookTranslation>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "BookTranslations",
BookStoreConsts.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new {x.BookId, x.Language});
});
```
> I haven't explicitly set up a one-to-many relationship between `Book` and `BookTranslation` here, but the entity framework will do it for us.
After that, you can just run the following command in a command-line terminal to add a new database migration (in the directory of the `EntityFrameworkCore` project):
```bash
dotnet ef migrations add Added_BookTranslation
```
This will add a new migration class to your project. You can then run the following command (or run the `.DbMigrator` application) to apply changes to the database:
```bash
dotnet ef database update
```
Add the following code to the `ConfigureServices` method of the `BookStoreEntityFrameworkCoreModule`:
```csharp
Configure<AbpEntityOptions>(options =>
{
options.Entity<Book>(bookOptions =>
{
bookOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Translations);
});
});
```
Now we can use `WithDetailsAsync` without any parameters on `BookAppService` knowing that `Translations` will be included.
#### Acme.BookStore.Application.Contracts
Implement `IObjectTranslation` in the `BookDto` class as follows:
```csharp
public class BookDto : AuditedEntityDto<Guid>, IObjectTranslation
{
public Guid AuthorId { get; set; }
public string AuthorName { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
public string Language { get; set; }
}
```
`Language` property is required to understand which language the translated book name belongs to in the UI.
Create the `AddBookTranslationDto` class in the `Books` folder as follows:
```csharp
public class AddBookTranslationDto : IObjectTranslation
{
[Required]
public string Language { get; set; }
[Required]
public string Name { get; set; }
}
```
Add the `AddTranslationsAsync` method to the `IBookAppService` as follows:
```csharp
public interface IBookAppService :
ICrudAppService<
BookDto,
Guid,
PagedAndSortedResultRequestDto,
CreateUpdateBookDto>
{
Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync();
Task AddTranslationsAsync(Guid id, AddBookTranslationDto input); // added this line
}
```
#### Acme.BookStore.Application
Now, we need to implement the `AddTranslationsAsync` method in `BookAppService` and include `Translations` in the `Book` entity, for this you can change the `BookAppService` as follows:
```csharp
[Authorize(BookStorePermissions.Books.Default)]
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto>, //Used to create/update a book
IBookAppService //implement the IBookAppService
{
private readonly IAuthorRepository _authorRepository;
public BookAppService(
IRepository<Book, Guid> repository,
IAuthorRepository authorRepository)
: base(repository)
{
_authorRepository = authorRepository;
GetPolicyName = BookStorePermissions.Books.Default;
GetListPolicyName = BookStorePermissions.Books.Default;
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Create;
}
public override async Task<BookDto> GetAsync(Guid id)
{
//Get the IQueryable<Book> from the repository
var queryable = await Repository.WithDetailsAsync(); // this line changed
//Prepare a query to join books and authors
var query = from book in queryable
join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
where book.Id == id
select new { book, author };
//Execute the query and get the book with author
var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
if (queryResult == null)
{
throw new EntityNotFoundException(typeof(Book), id);
}
var bookDto = ObjectMapper.Map<Book, BookDto>(queryResult.book);
bookDto.AuthorName = queryResult.author.Name;
return bookDto;
}
public override async Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
//Get the IQueryable<Book> from the repository
var queryable = await Repository.WithDetailsAsync(); // this line changed
//Prepare a query to join books and authors
var query = from book in queryable
join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
select new {book, author};
//Paging
query = query
.OrderBy(NormalizeSorting(input.Sorting))
.Skip(input.SkipCount)
.Take(input.MaxResultCount);
//Execute the query and get a list
var queryResult = await AsyncExecuter.ToListAsync(query);
//Convert the query result to a list of BookDto objects
var bookDtos = queryResult.Select(x =>
{
var bookDto = ObjectMapper.Map<Book, BookDto>(x.book);
bookDto.AuthorName = x.author.Name;
return bookDto;
}).ToList();
//Get the total count with another query
var totalCount = await Repository.GetCountAsync();
return new PagedResultDto<BookDto>(
totalCount,
bookDtos
);
}
public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
{
var authors = await _authorRepository.GetListAsync();
return new ListResultDto<AuthorLookupDto>(
ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
);
}
public async Task AddTranslationsAsync(Guid id, AddBookTranslationDto input)
{
var queryable = await Repository.WithDetailsAsync();
var book = await AsyncExecuter.FirstOrDefaultAsync(queryable, x => x.Id == id);
if (book.Translations.Any(x => x.Language == input.Language))
{
throw new UserFriendlyException($"Translation already available for {input.Language}");
}
book.Translations.Add(new BookTranslation
{
BookId = book.Id,
Name = input.Name,
Language = input.Language
});
await Repository.UpdateAsync(book);
}
private static string NormalizeSorting(string sorting)
{
if (sorting.IsNullOrEmpty())
{
return $"book.{nameof(Book.Name)}";
}
if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
{
return sorting.Replace(
"authorName",
"author.Name",
StringComparison.OrdinalIgnoreCase
);
}
return $"book.{sorting}";
}
}
```
Create the `MultiLingualBookObjectMapper` class as follows:
```csharp
public class MultiLingualBookObjectMapper : IObjectMapper<Book, BookDto>, ITransientDependency
{
private readonly MultiLingualObjectManager _multiLingualObjectManager;
private readonly ISettingProvider _settingProvider;
public MultiLingualBookObjectMapper(
MultiLingualObjectManager multiLingualObjectManager,
ISettingProvider settingProvider)
{
_multiLingualObjectManager = multiLingualObjectManager;
_settingProvider = settingProvider;
}
public BookDto Map(Book source)
{
var translation = AsyncHelper.RunSync(() =>
_multiLingualObjectManager.FindTranslationAsync<Book, BookTranslation>(source));
return new BookDto
{
Id = source.Id,
AuthorId = source.AuthorId,
Type = source.Type,
Name = translation?.Name ?? source.Name,
PublishDate = source.PublishDate,
Price = source.Price,
Language = translation?.Language ?? AsyncHelper.RunSync(() => _settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage)),
CreationTime = source.CreationTime,
CreatorId = source.CreatorId,
LastModificationTime = source.LastModificationTime,
LastModifierId = source.LastModifierId
};
}
public BookDto Map(Book source, BookDto destination)
{
return default;
}
}
```
To map the multi-lingual `Book` entity to `BookDto`, we implement custom mapping using the `IObjectMapper<TSource, TDestination>` interface. If no translation is found, default values are returned.
So far we have created the entire infrastructure. We don't need to change anything in the UI, if there is a translation according to the language chosen by the user, the list view will change. However, I want to create a simple modal where we can add new translations to an existing book in order to see what we have done.
#### Acme.BookStore.Web
Create a new razor page named `AddTranslationModal` in the `Books` folder as below.
**View**
```html
@page
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Web.Pages.Books.AddTranslationModal
@{
Layout = null;
}
<form asp-page="/Books/AddTranslationModal">
<abp-modal>
<abp-modal-header>Translations</abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id"></abp-input>
<abp-select asp-for="@Model.TranslationViewModel.Language" asp-items="Model.Languages" class="form-select">
<option selected value="">Pick a language</option>
</abp-select>
<abp-input asp-for="TranslationViewModel.Name"></abp-input>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel | AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</form>
```
**Model**
```csharp
public class AddTranslationModal : BookStorePageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid Id { get; set; }
public List<SelectListItem> Languages { get; set; }
[BindProperty]
public BookTranslationViewModel TranslationViewModel { get; set; }
private readonly IBookAppService _bookAppService;
private readonly ILanguageProvider _languageProvider;
public AddTranslationModal(
IBookAppService bookAppService,
ILanguageProvider languageProvider)
{
_bookAppService = bookAppService;
_languageProvider = languageProvider;
}
public async Task OnGetAsync()
{
Languages = await GetLanguagesSelectItem();
TranslationViewModel = new BookTranslationViewModel();
}
public async Task<IActionResult> OnPostAsync()
{
await _bookAppService.AddTranslationsAsync(Id, ObjectMapper.Map<BookTranslationViewModel, AddBookTranslationDto>(TranslationViewModel));
return NoContent();
}
private async Task<List<SelectListItem>> GetLanguagesSelectItem()
{
var result = await _languageProvider.GetLanguagesAsync();
return result.Select(
languageInfo => new SelectListItem
{
Value = languageInfo.CultureName,
Text = languageInfo.DisplayName + " (" + languageInfo.CultureName + ")"
}
).ToList();
}
public class BookTranslationViewModel
{
[Required]
[SelectItems(nameof(Languages))]
public string Language { get; set; }
[Required]
public string Name { get; set; }
}
}
```
Then, we can open the `BookStoreWebAutoMapperProfile` class and define the required mapping as follows:
```csharp
CreateMap<AddTranslationModal.BookTranslationViewModel, AddBookTranslationDto>();
```
Finally, change the content of `index.js` in the `Books` folder as follows:
```javascript
$(function () {
var l = abp.localization.getResource('BookStore');
var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal');
var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal');
var addTranslationModal = new abp.ModalManager(abp.appPath + 'Books/AddTranslationModal'); // added this line
var dataTable = $('#BooksTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
serverSide: true,
paging: true,
order: [[1, "asc"]],
searching: false,
scrollX: true,
ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),
columnDefs: [
{
title: l('Actions'),
rowAction: {
items:
[
{
text: l('Edit'),
visible: abp.auth.isGranted('BookStore.Books.Edit'),
action: function (data) {
editModal.open({ id: data.record.id });
}
},
{
text: l('Add Translation'), // added this action
visible: abp.auth.isGranted('BookStore.Books.Edit'),
action: function (data) {
addTranslationModal.open({ id: data.record.id });
}
},
{
text: l('Delete'),
visible: abp.auth.isGranted('BookStore.Books.Delete'),
confirmMessage: function (data) {
return l(
'BookDeletionConfirmationMessage',
data.record.name
);
},
action: function (data) {
acme.bookStore.books.book
.delete(data.record.id)
.then(function() {
abp.notify.info(
l('SuccessfullyDeleted')
);
dataTable.ajax.reload();
});
}
}
]
}
},
{
title: l('Name'),
data: "name"
},
{
title: l('Author'),
data: "authorName"
},
{
title: l('Type'),
data: "type",
render: function (data) {
return l('Enum:BookType:' + data);
}
},
{
title: l('PublishDate'),
data: "publishDate",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString();
}
},
{
title: l('Price'),
data: "price"
},
{
title: l('CreationTime'),
data: "creationTime",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_SHORT);
}
}
]
})
);
createModal.onResult(function () {
dataTable.ajax.reload();
});
editModal.onResult(function () {
dataTable.ajax.reload();
});
$('#NewBookButton').click(function (e) {
e.preventDefault();
createModal.open();
});
});
```
## Conclusion
With a multi-lingual application, you can expand your market share, but if not designed well, may your application will be unusable. So, I've tried to explain how to design a sustainable multi-lingual entity in this article.
### Source Code
You can find source code of the example solution used in this article [here](https://github.com/abpframework/abp-samples/tree/master/AcmeBookStoreMultiLingual).

BIN
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/data-model.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
docs/en/Community-Articles/15-08-2022-How-to-Design-Multi-Lingual-Entity/result.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

4
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

@ -1,9 +1,13 @@
# How to Add Custom Properties to the User Entity
> **Note:** If your application is greater than version 4.3.3, please follow [this article](../2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md).
## Introduction
In this step-by-step article, I will explain how you can customize the user entity class, which is available in every web application you create using the ABP framework, according to your needs. When you read this article, you will learn how to override the services of built-in modules, extend the entities, extend data transfer objects and customize the user interface in the applications you develop using the ABP framework.
> **Note:** This article is not about customizing the `Login` page. If you have such a need, please follow [this article](../2020-05-09-Customize-the-Login-Page-for-MVC-Razor-Page-Applications/POST.md).
You can see the screenshots below which we will reach at the end of the article.
![custom-identity-user-list](./custom-identity-user-list.png)

154
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md

@ -0,0 +1,154 @@
# How to Add Custom Properties to the User Entity
> **Note:** If your application is less than version 4.4.x, please follow [this article](../2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md).
## Introduction
In this step-by-step article, I will explain how you can customize the user entity class, which is available in every web application you create using the ABP framework, according to your needs. When you read this article, you will learn how to override the services of built-in modules, extend the entities, extend data transfer objects and customize the user interface in the applications you develop using the ABP framework.
> **Note:** This article is not about customizing the `Login` page. If you have such a need, please follow [this article](../2020-05-09-Customize-the-Login-Page-for-MVC-Razor-Page-Applications/POST.md).
You can see the screenshots below which we will reach at the end of the article.
![custom-identity-user-list](./custom-identity-user-list.png)
![new-user](./new-user.png)
## Preparing the Project
### Startup template and the initial run
Abp Framework offers startup templates to get into the work faster. We can create a new startup template using Abp CLI:
`abp new CustomizeUserDemo`
> In this article, I will go through the MVC application, but it will work also in the [Angular](https://docs.abp.io/en/abp/latest/Getting-Started?UI=NG&DB=EF&Tiered=No), [Blazor Server](https://docs.abp.io/en/abp/latest/Getting-Started?UI=BlazorServer&DB=EF&Tiered=No), and [Blazor WebAssembly](https://docs.abp.io/en/abp/latest/Getting-Started?UI=Blazor&DB=EF&Tiered=No) application.
After the download is finished, we can run **CustomizeUserDemo.DbMigrator** project to create the database migrations and seed the initial data (admin user, role, etc). Then we can run `CustomizeUserDemo.Web` to see that our application is working.
> Default admin username is **admin** and password is **1q2w3E\***
![initial-project](./initial-project.png)
In this article, we will go through a scenario together and find the solutions to our questions through this scenario. However, since the scenario is not a real-life scenario, it may be strange, please don't get too about this issue :)
## Step-1
Create the Users folder in the **CustomizeUserDemo.Domain.Shared** project, create the class `UserConsts` inside the folder and update the class you created as below:
```csharp
public static class UserConsts
{
public const string TitlePropertyName = "Title";
public const string ReputationPropertyName = "Reputation";
public const int MaxTitleLength = 64;
public const double MaxReputationValue = 1_000;
public const double MinReputationValue = 1;
}
```
## Step-2
Update the `CustomizeUserDemoEfCoreEntityExtensionMappings` class in the **CustomizeUserDemo.EntityFramework** project in the EntityFrameworkCore folder as below:
```csharp
public static class CustomizeUserDemoEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
CustomizeUserDemoGlobalFeatureConfigurator.Configure();
CustomizeUserDemoModuleExtensionConfigurator.Configure();
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
UserConsts.TitlePropertyName,
(_, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue("");
propertyBuilder.HasMaxLength(UserConsts.MaxTitleLength);
}
).MapEfCoreProperty<IdentityUser, int>(
UserConsts.ReputationPropertyName,
(_, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(UserConsts.MinReputationValue);
}
);
});
}
}
```
This class can be used to map these extra properties to table fields in the database. Please read [this](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities) article to improve your understanding of what we are doing.
So far, we have added our extra features to the `User` entity and matched these features with the `ef core`.
Now we need to add migration to see what has changed in our database. This for, open the Package Manager Console (PMC) under the menu Tools > NuGet Package Manager.
![nuget-package-manager](./nuget-package-manager.png)
Select the **CustomizeUserDemo.EntityFramework** as the **default project** and execute the following command:
```bash
Add-Migration "Updated-User-Entity"
```
![added-new-migration](./added-new-migration.png)
This will create a new migration class inside the `Migrations` folder of the **CustomizeUserDemo.EntityFrameworkCore** project.
> If you are using another IDE than the Visual Studio, you can use `dotnet-ef` tool as [documented here](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#create-a-migration).
Finally, run the **CustomizeUserDemo.DbMigrator** project to update the database.
When we updated the database, you can see that the `Title` and `Reputation` columns are added to the `Users` table.
![user-table](./user-table.png)
## Step-3
Open the `CustomizeUserDemoModuleExtensionConfigurator` in the **CustomizeUserDemo.Domain.Shared** project, and change the contents of the `ConfigureExtraProperties` method as shown below:
```csharp
private static void ConfigureExtraProperties()
{
ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>(
UserConsts.TitlePropertyName,
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(UserConsts.MaxTitleLength)
);
}
);
user.AddOrUpdateProperty<int>(
UserConsts.ReputationPropertyName,
options =>
{
options.DefaultValue = UserConsts.MinReputationValue;
options.Attributes.Add(
new RangeAttribute(UserConsts.MinReputationValue, UserConsts.MaxReputationValue)
);
}
);
});
});
}
```
That's it. Now let's run the application and look at the Identity user page. You can also try to edit and recreate a record if you want, it will work even though we haven't done anything extra. Here is the magic code behind ABP framework.
If there is a situation you want to add, you can click the contribute button or make a comment. Also, if you like the article, don't forget to share it :)
Happy coding :)

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/added-new-migration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/custom-identity-user-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/initial-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/new-user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/nuget-package-manager.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/en/Community-Articles/2022-07-19-How-To-Add-Custom-Property-To-The-User-Entity/user-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

34
docs/en/Dependency-Injection.md

@ -244,25 +244,35 @@ One restriction of property injection is that you cannot use the dependency in y
Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class.
### Resolve Service from IServiceProvider
#### DisablePropertyInjectionAttribute
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties.
````C#
[DisablePropertyInjection]
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public ITaxCalculator TaxCalculator { get; set; }
}
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
[DisablePropertyInjection]
public ITaxCalculator TaxCalculator { get; set; }
}
````
### Resolve Service from IServiceProvider
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
````C#
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
}
````

452
docs/en/Deploy-azure-app-service.md

@ -0,0 +1,452 @@
# Deploying ABP Project to Azure App Service
In this document, you will learn how to create and deploy your first ABP web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. ABP web apps are cross-platform and can be hosted on Linux, Windows or MacOS.
****Prerequisites****
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
- A GitHub account [Create an account for free](http://github.com/).
## Creating a new ABP application
Create a repository on [GitHub.com](https://github.com/) (keep all settings as default).
Open the command prompt and clone the repository into a folder on your computer
```bash
git clone https://github.com/your-username/your-repository-name.git
```
Check your dotnet version. It should be at least 3.1.x
```bash
dotnet --version
```
Install or update the [ABP CLI](https://docs.abp.io/en/abp/latest/cli) with the following command:
```bash
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
```
Open the command prompt in the *GitHub repository folder* and create a new ABP Blazor solution with the command below:
```bash
abp new YourAppName -u blazor
```
## Running the application
Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations:
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project:
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project:
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project and you should see the landing page.
Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
## Committing to GitHub
Before the GitHub commit, you have to delete the line "**/wwwroot/libs/*" at *.gitignore* file.
![azdevops-23](images/azdevops-23.png)
Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository:
```bash
git add .
git commit -m initialcommit
git push
```
## Configuring Azure database connection string
Create a SQL database on Azure and change the connection string in all the *appsettings.json* files.
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *SQL Database*
* Click the **Create** button in the *SQL Database window*
* Create a new resource group. Name it *rg[YourAppName]*
* Enter *[YourAppName]Db* as database name
* Create a new Server and name it *[yourappname]server*
* Enter a serveradmin login and passwords. Click the **OK** button
* Select your *Location*
* Check *Allow Azure services to access server*
* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
* Click the **Review + create** button. Click **Create**
* Click **Go to resource** and click **SQL server** when the SQL Database is created
* Click **Networking** under Security left side menu
* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
* Select **Allow Azure and resources to access this seerver** and save
* Go to your **SQL database**, click **Connection strings** and copy the connection string
* Copy/paste the *appsettings.json* files of the *[YourAppName].HttpApi.Host* and the *[YourAppName].DbMigrator* project
* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
## Running DB Migrations
Open the command prompt in the *[YourAppName].DbMigrator* project again and enter the command below to apply the database migrations:
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project and enter the command below to check your API is working:
```bash
dotnet run
```
Stop the *[YourAppName].HttpApi.Host* by pressing CTRL+C.
## Committing to GitHub
Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Setting up the Build pipeline in AzureDevops and publish the Build Artifacts
* Sign in Azure DevOps
* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
* Select **Public visibility** and click the **Create project** button
* Click the **Pipelines** button to continue
* Click the **Create Pipeline** button
Select GitHub in the Select your repository window
![azdevops-1](images/azdevops-1.png)
* Enter the Connection name. *[YourAppName]GitHubConnection* and click **Authorize using OAuth**
* Select your **GitHub** [YourAppName]repo and click Continue
* Search for **ASP.NET** in the ***Select a template*** window
![azdevops-2](images/azdevops-2.png)
* Select the ASP.NET Core template and click the **Apply** button
* Add the below commands block as a first step in the pipeline
```
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
```
![azdevops-18](images/azdevops-18.png)
* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
![azdevops-3](images/azdevops-3.png)
* Add the below commands block to the end of the pipeline
```
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
condition: succeededOrFailed()
```
![azdevops-4](images/azdevops-4.png)
```
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'NuGet.config'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
publishLocation: 'Container'
condition: succeededOrFailed()
```
* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
* When the Build pipeline has finished. Click **1 published; 1 consumed**
## Creating a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
* Search for Web App in the *Search the Marketplace* field
* Click the **Create** button in the Web App window
* Select rg[YourAppName] in the *Resource Group* dropdown
* Enter [YourAppName]API in the *Name input* field
* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
* Enter [YourAppName]API in the *Name input* field
* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
* Select Windows as *Operating System*
* Select the same *Region* as in the SQL server you created in Part 3
![azdevops-5](images/azdevops-5.png)
* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
![azdevops-6](images/azdevops-6.png)
* Click the **Review + create** button. Click the **Create** button
* Click **Go to resource** when the Web App has been created
## Creating a release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
* Click the **New pipeline** button in the *No release pipelines found* window
* Select *Azure App Service deployment* and click the **Apply** button
![azdevops-7](images/azdevops-7.png)
* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
* Click **+ Add an artifact** in the *Pipeline* tab
* Select the **Build** icon as *Source type* in the *Add an artifact* window
* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
![azdevops-8](images/azdevops-8.png)
* Click the **Continuous deployment trigger (thunderbolt icon)**
* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
![azdevops-9](images/azdevops-9.png)
* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
* Select your subscription in the *Azure subscription* dropdown.
![azdevops-10](images/azdevops-10.png)
* Click **Authorize** and enter your credentials in the next screens
* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
* Click the **Deploy Azure App Service** task
* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
![azdevops-11](images/azdevops-11.png)
* Click the **Save** icon in the top menu and click **OK**
* Click **Create release** in the top menu. Click **Create** to create a release
* Click the *Pipeline* tab and wait until the Deployment succeeds
![azdevops-12](images/azdevops-12.png)
* Open a browser and navigate to the URL of your Web App
```
https://[YourAppName]api.azurewebsites.net
```
![azdevops-13](images/azdevops-13.png)
## Creating a Web App in Azure Portal to deploy [YourAppName].Blazor project
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *Web App* in the *Search the Marketplace* field
* Click the **Create** button in the *Web App* window
* Select *rg[YourAppName]* in the *Resource Group* dropdown
* Enter *[YourAppName]Blazor* in the *Name* input field
* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
* Select *Windows* as *Operating System*
* Select the same region as the SQL server you created in Part 3
* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
![azdevops-14](images/azdevops-14.png)
* Click the **Review + create** button. Click **Create** button
* Click **Go to resource** when the Web App has been created
* Copy the URL of the Blazor Web App for later use
```
https://[YourAppName]blazor.azurewebsites.net
```
## Changing the Web App configuration for the Azure App Service
Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
![azdevops-19](images/azdevops-19.png)
![azdevops-20](images/azdevops-20.png)
![azdevops-21](images/azdevops-21.png)
## Adding an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
* Click the **+ Add** link and add a **New Stage**
![azdevops-15](images/azdevops-15.png)
* Select *Azure App Service deployment* and click the **Apply** button
* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
* Select your subscription in the *Azure subscription* dropdown
* Select your Blazor Web App in the *App service name* dropdown
* Click the **Deploy Azure App Service task**
* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
![azdevops-16](images/azdevops-16.png)
* Click **Save** in the top menu and click the **OK** button after
* Click **Create release** in the top menu and click the **Create** button
![azdevops-17](images/azdevops-17.png)
![azdevops-22](images/azdevops-22.png)

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

@ -141,13 +141,14 @@ Configure<AbpRabbitMqOptions>(options =>
});
````
**Example: Configure the client and exchange names**
**Example: Configure the client, exchange names and prefetchCount**
````csharp
Configure<AbpRabbitMqEventBusOptions>(options =>
{
options.ClientName = "TestApp1";
options.ExchangeName = "TestMessages";
options.PrefetchCount = 1;
});
````

15
docs/en/Entities.md

@ -112,6 +112,21 @@ For the example above, the composite key is composed of `UserId` and `RoleId`. F
> Also note that Entities with Composite Primary Keys cannot utilize the `IRepository<TEntity, TKey>` interface since it requires a single Id property. However, you can always use `IRepository<TEntity>`. See [repositories documentation](Repositories.md) for more.
### EntityEquals
`Entity.EntityEquals(...)` method is used to check if two Entity Objects are equals.
Example:
```csharp
Book book1 = ...
Book book2 = ...
if (book1.EntityEquals(book2)) //Check equality
{
...
}
```
## AggregateRoot Class

1
docs/en/Multi-Tenancy.md

@ -222,7 +222,6 @@ The following resolvers are provided and configured by default;
* `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**.
* `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default.
* `FormTenantResolveContributor`:Tries to find current tenant id from form parameters. The parameter name is `__tenant` by default.
* `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route.
* `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default.
* `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default.

155
docs/en/Themes/LeptonXLite/AspNetCore.md

@ -9,22 +9,22 @@ LeptonX Lite has implementation for the ABP Framework Razor Pages. It's a simpli
This theme is **already installed** when you create a new solution using the startup templates. If you are using any other template, you can install this theme by following the steps below:
- Add **Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite** package to your **Web** application.
- Add the **Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite** package to your **Web** application.
```bash
dotnet add package Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite --prerelease
```
- Remove **Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic** reference from the project since it's not necessary after switching to LeptonX Lite.
- Remove the **Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic** reference from the project since it's not necessary after switching to LeptonX Lite.
- Make sure the old theme is removed and LeptonX is added in your Module class.
```diff
[DependsOn(
// Remove BasicTheme module from DependsOn attribute
// Remove the BasicTheme module from DependsOn attribute
- typeof(AbpAspNetCoreMvcUiBasicThemeModule),
// Add LeptonX Lite module to DependsOn attribute
// Add the LeptonX Lite module to DependsOn attribute
+ typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
)]
```
@ -35,9 +35,9 @@ dotnet add package Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite --prerelease
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles.Configure(
// Remove following line
// Remove the following line
- BasicThemeBundles.Styles.Global,
// Add following line instead
// Add the following line instead
+ LeptonXLiteThemeBundles.Styles.Global
bundle =>
{
@ -47,10 +47,24 @@ Configure<AbpBundlingOptions>(options =>
});
```
---
## Customization
### Layouts
LeptonX Lite Mvc provides **layouts** for your **user interface** based [ABP Framework Theming](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Theming). You can use **layouts** to **organize your user interface**.
The main responsibility of a theme is to **provide** the layouts. There are **three pre-defined layouts that must be implemented by all the themes:**
* **Application:** The **default** layout which is used by the **main** application pages.
* **Account:** Mostly used by the **account module** for **login**, **register**, **forgot password**... pages.
* **Empty:** The **Minimal** layout that **has no layout components** at all.
**Layout names** are **constants** defined in the `LeptonXLiteTheme` class in the **Mvc** project **root**.
> The layout pages define under the `Themes/LeptonXLite/Layouts` folder and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
### Toolbars
LeptonX Lite includes separeted toolbars for desktop & mobile. You can manage toolbars independently. Toolbar names can be accessible in the **LeptonXLiteToolbars** class.
@ -74,3 +88,128 @@ public class MyProjectNameMainToolbarContributor : IToolbarContributor
}
}
```
# LeptonX Lite Mvc Components
Abp **helps** you make **highly customizable UI**. You can easily **customize** your themes to fit your needs. **The Virtual File System** makes it possible to **manage files** that **do not physically** exist on the **file system** (disk). It's mainly used to embed **(js, css, image..)** files into assemblies and **use them like** physical files at runtime. An application (or another module) can **override** a **virtual file of a module** just like placing a file with the **same name** and **extension** into the **same folder** of the **virtual file**.
LeptonX Lite is built on the [Abp Framework](https://abp.io/), so you can **easily** customize your Asp.Net Core Mvc user interface by following [Abp Mvc UI Customization](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-xUser-Interface).
## Brand Component
The **brand component** is a simple component that can be used to display your brand. It contains a **logo** and a **company name**.
![Brand component](../../images/leptonxlite-brand-component.png)
### How to override the Brand Component in LeptonX Lite Mvc
* The **brand component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Brand/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **brand component (C# file)** is defined in the `Themes/LeptonXLite/Components/Brand/MainNavbarBrandViewComponent.cs` file and you can **override it** by creating a file with the **same name** and under the **same folder**.
## Breadcrumb Component
On websites that have a lot of pages, **breadcrumb navigation** can greatly **enhance the way users find their way** around. In terms of **usability**, breadcrumbs reduce the number of actions a website **visitor** needs to take in order to get to a **higher-level page**, and they **improve** the **findability** of **website sections** and **pages**.
![Breadcrumb component](../../images/leptonxlite-breadcrumb-component.png)
### How to override the Breadcrumb Component in LeptonX Lite Mvc
* The **breadcrumb component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Breadcrumbs/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **breadcrumb component (C# file)** is defined in the `Themes/LeptonXLite/Components/Breadcrumbs/BreadcrumbsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Sidebar Menu Component
Sidebar menus have been used as **a directory for Related Pages** to a **Service** offering, **Navigation** items to a **specific service** or topic and even just as **Links** the user may be interested in.
![Sidebar menu component](../../images/leptonxlite-sidebar-menu-component.png)
### How to override the Sidebar Menu Component in LeptonX Lite Mvc
* **Sidebar menu page (.cshtml)** is defined in the `Themes/LeptonXLite/Components/Menu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* If you want to **override the menu component (C#)** you can override the `Themes/LeptonXLite/Components/Menu/MainMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
> The **sidebar menu** renders menu items **dynamically**. The **menu item** is a **partial view** and is defined in the `Themes/LeptonXLite/Components/Menu/_MenuItem.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Page Alerts Component
Provides contextual **feedback messages** for typical user actions with the handful of **available** and **flexible** **alert messages**. Alerts are available for any length of text, as well as an **optional dismiss button**.
![Page alerts component](../../images/leptonxlite-page-alerts-component.png)
### How to override the Page Alerts Component in LeptonX Lite Mvc
* The **page alerts component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/PageAlerts/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **page alerts component (C#)** is defined in the `Themes/LeptonXLite/Components/PageAlerts/PageAlertsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Toolbar Component
Toolbar items are used to add **extra functionality to the toolbar**. The toolbar is a **horizontal bar** that **contains** a group of **toolbar items**.
### How to override the Toolbar Component in LeptonX Lite Mvc
* The **toolbar component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/Toolbar/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **toolbar component (C#)** is defined in the `Themes/LeptonXLite/Components/Toolbar/ToolbarViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Toolbar Item Component
The toolbar item is a **single item** that **contains** a **link**, an **icon**, a **label** etc..
### How to override the Toolbar Item Component in LeptonX Lite Mvc
* The **toolbar item component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/ToolbarItems/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **toolbar item component (C#)** is defined in the `Themes/LeptonXLite/Components/ToolbarItems/ToolbarItemsViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
You can find the toolbar components below:
## Language Switch Component
Think about a **multi-lingual** website and the first thing that could **hit your mind** is **the language switch component**. A **navigation bar** is a **great place** to **embed a language switch**. By embedding the language switch in the navigation bar of your website, you would **make it simpler** for users to **find it** and **easily** switch the **language** <u>**without trying to locate it across the website.**</u>
![Language switch component](../../images/leptonxlite-language-switch-component.png)
### How to override the Language Switch Component in LeptonX Lite Mvc
* The **language switch component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/LanguageSwitch/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **language switch component (C#)** is defined in the `Themes/LeptonXLite/Components/LanguageSwitch/LanguageSwitchViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Mobile Language Switch Component
The **mobile** **language switch component** is used to switch the language of the website **on mobile devices**. The mobile language switch component is a **dropdown menu** that **contains all the languages** of the website.
![Mobil language switch component](../../images/leptonxlite-mobile-language-switch-component.png)
### How to override the Mobile Language Switch Component in LeptonX Lite Mvc
* The **mobile language switch component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/MobileLanguageSwitch/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **mobile language switch component (C#)** is defined in the `Themes/LeptonXLite/Components/MobileLanguageSwitch/MobileLanguageSwitchViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## User Menu Component
The **User Menu** is the **menu** that **drops down** when you **click your name** or **profile picture** in the **upper right corner** of your page (**in the toolbar**). It drops down options such as **Settings**, **Logout**, etc.
![User menu component](../../images/leptonxlite-user-menu-component.png)
### How to override the User Menu Component in LeptonX Lite Mvc
* The **user menu component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/UserMenu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **user menu component (C#)** is defined in the `Themes/LeptonXLite/Components/UserMenu/UserMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
## Mobile User Menu Component
The **mobile user menu component** is used to display the **user menu on mobile devices**. The mobile user menu component is a **dropdown menu** that contains all the **options** of the **user menu**.
![Mobile user menu component](../../images/leptonxlite-mobile-user-menu-component.png)
### How to override the Mobile User Menu Component in LeptonX Lite Mvc
* The **mobile user menu component page (.cshtml file)** is defined in the `Themes/LeptonXLite/Components/MobileUserMenu/Default.cshtml` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.
* The **mobile user menu component (C#)** is defined in the `Themes/LeptonLite/Components/MobileUserMenu/MobileUserMenuViewComponent.cs` file and you can **override it** by creating a file with the **same name** and **under** the **same folder**.

BIN
docs/en/images/leptonxlite-brand-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/images/leptonxlite-breadcrumb-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/images/leptonxlite-language-switch-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/images/leptonxlite-mobile-language-switch-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/images/leptonxlite-mobile-user-menu-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/images/leptonxlite-page-alerts-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/images/leptonxlite-sidebar-menu-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/images/leptonxlite-user-menu-component.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

140
docs/es/Getting-Started-AspNetCore-Application.md

@ -0,0 +1,140 @@
# Empezando con ABP y una Aplicacion AspNet Core MVC Web
Este tutorial explica como empezar una aplicacion ABP desde cero usando las dependencias minimas. Uno generalmente desea
empezar con la **[plantilla de inicio](Getting-Started-AspNetCore-MVC-Template.md)**.
## Crea un Proyecto Nuevo
1. Crea una Aplicacion Web AspNet Core nueva usando Visual Studio 2022 (17.0.0+):
![](images/create-new-aspnet-core-application-v2.png)
2. Configura el nuevo proyecto:
![](images/select-empty-web-application-v2.png)
3. Presione el boton Create:
![create-aspnet-core-application](images/create-aspnet-core-application.png)
## Instale el paquete Volo.Abp.AspNetCore.Mvc
Volo.Abp.AspNetCore.Mvc es el paquete de integracion con AspNet Core MVC para ABP. Siendo asi, instalalo en su proyecto:
````
Install-Package Volo.Abp.AspNetCore.Mvc
````
## Crea el primer modulo ABP
ABP es un marco de referencia modular y require una clase de **inicio (raíz) tipo modulo** derivada de ``AbpModule``:
````C#
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
namespace BasicAspNetCoreApplication
{
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
public class AppModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
// Configura la canalización de peticiones HTTP.
if (env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// El valor por defecto de HSTS es 30 dias. Debes cambiar esto en ambientes productivos. Referencia https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseConfiguredEndpoints();
}
}
}
````
``AppModule`` es un buen nombre para el modulo de inicio de una aplicacion.
Los paquetes de ABP definen clases de tipo modulo y cada modulo puede depender de otro.
En el codigo anterior, el ``AppModule`` depende de el modulo ``AbpAspNetCoreMvcModule`` (definido por el paquete [Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc)). Es comun agregar el atributo ``DependsOn`` despues de instalar un paquete ABP nuevo.
En vez de la clase de inicion Startup, estamos configurando una canalizacion de ASP.NET Core en este modulo.
## La clase Program
El proximo paso es modificar la clase Program para integrate el sistema de modulos ABP:
````C#
using BasicAspNetCoreApplication;
var builder = WebApplication.CreateBuilder(args);
await builder.Services.AddApplicationAsync<AppModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
````
``builder.Services.AddApplicationAsync<AppModule>();`` Agrega todos los servicios definidos en todos los modulos empezando desde ``AppModule``.
``app.InitializeApplicationAsync()`` inicializa y empieza la aplicacion.
## Ejecutar la Aplicación
Es todo! Ejecuta la aplicación, debe funcionar como esperado.
## Uso de Autofac como Marco de Inyección de Dependencia
Mientras el sistema de Inyección de Dependencia de ASP.NET Core es suficiente para requerimientos basico, [Autofac](https://autofac.org/) proporciona características avanzadas como Inyección de Propiedades e Intercepcion de Metodos, los cuales son necesarios para que ABP pueda llevar a cabo funciones avanzadas.
El acto de remplazar el sistema DI de ASP.NET Core por Autofac e integrarlo con ABP es facil.
1. Instala el paquete [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac)
````
Install-Package Volo.Abp.Autofac
````
2. Agrega la dependencia sobre el modulo ``AbpAutofacModule``
````C#
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
[DependsOn(typeof(AbpAutofacModule))] //Agrega la dependencia sobre el modulo ABP Autofac
public class AppModule : AbpModule
{
...
}
````
3. Actualiza `Program.cs` para que use Autofac:
````C#
using BasicAspNetCoreApplication;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac(); //Agrega esta linea
await builder.Services.AddApplicationAsync<AppModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
````
## Codigo fuente
Obten el codigo fuente del ejemplo creado en este tutorial de [aqui](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication).

BIN
docs/es/images/create-aspnet-core-application.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
docs/es/images/create-new-aspnet-core-application-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

BIN
docs/es/images/select-empty-web-application-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

183
docs/pt-BR/Validation.md

@ -1,3 +1,182 @@
## Validation
# Validação
Façam
O sistema de validação é utilizado para validar a entrada do usuário ou a requisição do cliente para uma ação de um controller ou por um serviço.
O ABP é compatível com o sistema de Validação de Modelos do ASP.NET Core e tudo escrito na [sua documentação](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) é válido para aplicações baseadas no ABP. Logo, esse documento foca nas funcionalidades do ABP ao invés de repetir a documentação da Microsoft.
Além disso, o ABP adiciona os seguintes benefícios:
* Define `IValidationEnabled` para adicionar validação automática para uma classe qualquer. Como todos os [serviços de aplicação](Application-Services.md) já o implementam, eles também são validados automaticamente.
* Automaticamente traduz os erros de validação para os atributos de anotação de dados.
* Provê serviços extensíveis para validar a chamada de um método ou o estado de um objeto.
* Provê integração com o [FluentValidation](https://fluentvalidation.net/)
## Validando DTOs
Essa seção introduz brevemente o sistema de validação. Para mais detalhes, veja a [Documentação da Validação de Modelo em ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation).
### Atributos de anotação de dados
Utilizar anotações de dados é uma maneira simples de implementar uma validação formal para um [DTO](Data-Transfer-Objects.md) de uma forma declarativa. Exemplo:
````csharp
public class CreateBookDto
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
}
````
Quando você utilizar essa classe como parâmetro para um [serviço da aplicação](Application-Services.md) ou um controller, ele será automaticamente validado e a validação traduzida será lançada ([e tratada](Exception-Handling.md) pelo ABP framework).
### IValidatableObject
`IValidatableObject` pode ser implementado por um DTO para executar uma lógica customizada de validação. O `CreateBookDto` no exemplo a seguir implementa essa interface e verifica se o `Name` é igual a `Description` e retorna um erro de validação nesse caso.
````csharp
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Acme.BookStore
{
public class CreateBookDto : IValidatableObject
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (Name == Description)
{
yield return new ValidationResult(
"Name and Description can not be the same!",
new[] { "Name", "Description" }
);
}
}
}
}
````
#### Resolvendo um serviço.
Se você precisar resolver um serviço do [sistema de injeção de dependências](Dependency-Injection.md), você pode utilizar o objeto `ValidationContext`.
````csharp
var myService = validationContext.GetRequiredService<IMyService>();
````
> Enquanto resolver os serviços no método `Validate` permite várias possibilidades, não é um boa prática implementar sua lógica de validação do domínio nos DTOs. Mantenha os DTOs simples. Seu propósito é transferir dados (DTO: Data Transfer Object, ou Objeto de Transferência de Dados).
## Infraestrutura de Validação.
Essa seção explica alguns serviços adicionais fornecidos pelo ABP Framework.
### Interface IValidationEnabled
`IValidationEnabled` é um marcador vazio de interface que pode ser implementado por qualquer classe (registrada e resolvida a partir do [DI](Dependency-Injection.md)) para permitir que o ABP framework realize o sistema de validação para os métodos da classe. Por exemplo:
````csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;
namespace Acme.BookStore
{
public class MyService : ITransientDependency, IValidationEnabled
{
public virtual async Task DoItAsync(MyInput input)
{
//...
}
}
}
````
> O ABP framework utiliza o sistema de [Proxying Dinâmico / Interceptadores](Dynamic-Proxying-Interceptors.md) para realizar a validação. Para fazê-lo funcionar, seu método deve ser **virtual** ou seu serviço deve ser injetado e utilizado através de uma **interface** (como `IMyService`).
#### Habilitando e Desabilitando Validações
Você pode utilizar o `[DisableValidation]` e desabilitar a validação para métodos, classes e propriedades.
````csharp
[DisableValidation]
public Void MyMethod()
{
}
[DisableValidation]
public class InputClass
{
public string MyProperty { get; set; }
}
public class InputClass
{
[DisableValidation]
public string MyProperty { get; set; }
}
````
### AbpValidationException
Uma vez que o ABP determina um erro de validação, é lançada uma validação do tipo `AbpValidationException`. O código da sua aplicação poderá lançar o `AbpValidationException`, mas na maioria das vezes não será necessário.
* A propriedade `ValidationErrors` do `AbpValidationException` contem a lista com os erros de validação.
* O nível de log do `AbpValidationException` é definido como `Warning`. Todos os erros de validação são logados no [Sistema de Logging](Logging.md).
* `AbpValidationException` é tratado automaticamente pelo ABP framework e é convertido em um erro utilizável com o código de status HTTP 400. Veja a documentação de [Manipulação de Exceção](Exception-Handling.md) para mais informações.
## Tópicos Avançados
### IObjectValidator
Além da validação automática, você pode querer validar um objeto manualmente. Nesse caso, [injete](Dependency-Injection.md) e use o serviço `IObjectValidator`:
* O método `ValidateAsync` valida o objeto informado baseado nas regras de validação e lança uma `AbpValidationException` se não estiver em um estado válido.
* `GetErrorsAsync` não lança uma exceção, somente retorna os erros de validação.
`IObjectValidator` é implementado pelo `ObjectValidator` por padrão. `ObjectValidator` é extensível; você pode implementar a interface `IObjectValidationContributor` para contribuir com uma lógica customizada. Exemplo:
````csharp
public class MyObjectValidationContributor
: IObjectValidationContributor, ITransientDependency
{
public Task AddErrorsAsync(ObjectValidationContext context)
{
//Get the validating object
var obj = context.ValidatingObject;
//Add the validation errors if available
context.Errors.Add(...);
return Task.CompletedTask;
}
}
````
* Lembre-se de registrar sua classe no [DI](Dependency-Injection.md) (implementar `ITransientDependency` faz isso no exemplo anterior)
* ABP vai automaticamente descobrir sua classe e utilizá-la em qualquer tipo de validação de objetos (incluindo chamadas de métodos de validação automáticas).
### IMethodInvocationValidator
`IMethodInvocationValidator` é utilizado para validar a chamada de um método. Ele utiliza internamente o `IObjectValidator` para validar os objetos passados na chamada do método. Você normalmente não precisa deste serviço, já que ele é utilizado automaticamente pelo framework, mas você pode querer reutilizar ou substituir na sua aplicação em alguns casos raros.
## Integração com FluentValidation
O pacote Volo.Abp.FluentValidation integra a biblioteca FluentValidation com o sistema de validação (implementando o `IObjectValidationContributor`). Veja o [documento de Integração com o FluentValidation](FluentValidation.md) para mais informações.

35
docs/zh-Hans/Background-Jobs-Hangfire.md

@ -66,6 +66,41 @@ public class YourModule : AbpModule
}
````
### 指定队列
你可以使用 [`QueueAttribute`](https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html) 来指定队列.
````csharp
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
namespace MyProject
{
[Queue("alpha")]
public class EmailSendingJob
: AsyncBackgroundJob<EmailSendingArgs>, ITransientDependency
{
private readonly IEmailSender _emailSender;
public EmailSendingJob(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public override async Task ExecuteAsync(EmailSendingArgs args)
{
await _emailSender.SendAsync(
args.EmailAddress,
args.Subject,
args.Body
);
}
}
}
````
1. 如果你想要使用Hangfire的面板,你可以在 `Module` 类的 `OnApplicationInitialization` 方法添加: `UseHangfireDashboard`
````csharp

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

@ -126,25 +126,31 @@ Configure<AbpRabbitMqOptions>(options =>
Configure<AbpRabbitMqBackgroundJobOptions>(options =>
{
options.DefaultQueueNamePrefix = "my_app_jobs.";
options.DefaultDelayedQueueNamePrefix = "my_app_jobs.delayed"
options.PrefetchCount = 1;
options.JobQueues[typeof(EmailSendingArgs)] =
new JobQueueConfiguration(
typeof(EmailSendingArgs),
queueName: "my_app_jobs.emails",
connectionName: "SecondConnection"
connectionName: "SecondConnection",
delayedQueueName:"my_app_jobs.emails.delayed"
);
});
```
- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业.
- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`并且设置默认的延迟队列名为 `my_app_jobs.delayed`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业.
- 设置了预取数量, 用于所有队列.
- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接.
`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数.
- `queueName`: 指定后台作业对应的队列名称(全名).
* `DelayedQueueName`: 指定后台延迟执行的作业对于的队列名称(全名).
- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`.
- `durable`: 可选参数,默认为 `true`.
- `exclusive`: 可选参数,默认为 `false`.
- `autoDelete`: 可选参数,默认为 `false`.
* `PrefetchCount` (可选参数, 默认为: null)
如果你想要更多地了解 `durable`,`exclusive`,`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档.

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

@ -141,13 +141,14 @@ Configure<AbpRabbitMqOptions>(options =>
});
````
**示例: 配置客户端和交换机名称**
**示例: 配置客户端,交换机名称和预取数量**
````csharp
Configure<AbpRabbitMqEventBusOptions>(options =>
{
options.ClientName = "TestApp1";
options.ExchangeName = "TestMessages";
options.PrefetchCount = 1;
});
````

16
docs/zh-Hans/Entities.md

@ -105,6 +105,22 @@ public class UserRole : Entity
> 需要注意,复合主键实体不可以使用 `IRepository<TEntity, TKey>` 接口,因为它需要一个唯一的Id属性. 但你可以使用 `IRepository<TEntity>`.更多信息请参见[仓储](Repositories.md)的文档.
### EntityEquals
`Entity.EntityEquals(...)` 方法用于检查两个实体对象是否相等.
示例:
```csharp
Book book1 = ...
Book book2 = ...
if (book1.EntityEquals(book2)) //Check equality
{
...
}
```
## 聚合根
"*聚合是域驱动设计中的一种模式.DDD的聚合是一组可以作为一个单元处理的域对象.例如,订单及订单系列的商品,这些是独立的对象,但将订单(连同订单系列的商品)视为一个聚合通常是很有用的*"( [查看详细介绍](http://martinfowler.com/bliki/DDD_Aggregate.html))

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

@ -322,7 +322,6 @@ Volo.Abp.AspNetCore.MultiTenancy 添加了下面这些租户解析器,从当前W
* **CurrentUserTenantResolveContributor**: 如果当前用户已登录,从当前用户的声明中获取租户Id. **出于安全考虑,应该始终将其做为第一个Contributor**.
* **QueryStringTenantResolveContributor**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant".
* **FormTenantResolveContributor**: 尝试从form参数中获取当前租户,默认参数名为"__tenant".
* **RouteTenantResolveContributor**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户.
* **HeaderTenantResolveContributor**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant".
* **CookieTenantResolveContributor**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant".

42
framework/Volo.Abp.sln

@ -409,6 +409,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.RemoteServices", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.PlugIn", "test\Volo.Abp.AspNetCore.Mvc.PlugIn\Volo.Abp.AspNetCore.Mvc.PlugIn.csproj", "{C6D6D878-208A-4FD2-822E-365545D8681B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Dapr", "src\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj", "{192A829F-D608-4E41-8DE0-058E943E453F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Dapr", "src\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj", "{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Dapr", "src\Volo.Abp.Http.Client.Dapr\Volo.Abp.Http.Client.Dapr.csproj", "{18B796D2-D45D-41AE-9A42-75C9B14B20DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr", "src\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj", "{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr.EventBus", "src\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj", "{B02EF042-C39E-45C4-A92D-BF7554E1889D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking.Dapr", "src\Volo.Abp.DistributedLocking.Dapr\Volo.Abp.DistributedLocking.Dapr.csproj", "{CAE48068-233C-47A9-BEAB-DDF521730E7A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1219,6 +1231,30 @@ Global
{C6D6D878-208A-4FD2-822E-365545D8681B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6D6D878-208A-4FD2-822E-365545D8681B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6D6D878-208A-4FD2-822E-365545D8681B}.Release|Any CPU.Build.0 = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.Build.0 = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.Build.0 = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.Build.0 = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.Build.0 = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.Build.0 = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1425,6 +1461,12 @@ Global
{3683340D-92F5-4B14-B77B-34A163333309} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{EDFFDA74-090D-439C-A58D-06CCF86D4423} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{C6D6D878-208A-4FD2-822E-365545D8681B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{192A829F-D608-4E41-8DE0-058E943E453F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{18B796D2-D45D-41AE-9A42-75C9B14B20DF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B02EF042-C39E-45C4-A92D-BF7554E1889D} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{CAE48068-233C-47A9-BEAB-DDF521730E7A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

7
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor

@ -0,0 +1,7 @@
@if (LayoutHookViewModel.Hooks.Any())
{
foreach (var hook in LayoutHookViewModel.Hooks)
{
<DynamicComponent Type="@hook.ComponentType" />
}
}

39
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Components/LayoutHooks/LayoutHook.razor.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Volo.Abp.Ui.LayoutHooks;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Components.LayoutHooks;
public partial class LayoutHook : ComponentBase
{
[Parameter]
public string Name { get; set; }
[Parameter]
public string Layout { get; set; }
[Inject]
protected IOptions<AbpLayoutHookOptions> LayoutHookOptions { get; set; }
protected LayoutHookViewModel LayoutHookViewModel { get; private set; }
protected override Task OnInitializedAsync()
{
if (LayoutHookOptions.Value.Hooks.TryGetValue(Name, out var layoutHooks))
{
layoutHooks = layoutHooks
.WhereIf(string.IsNullOrWhiteSpace(Layout), x => x.Layout == Layout)
.ToList();
}
layoutHooks ??= new List<LayoutHookInfo>();
LayoutHookViewModel = new LayoutHookViewModel(layoutHooks.ToArray(), Layout);
return Task.CompletedTask;
}
}

6
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/StandardLayouts.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Layout;
public static class StandardLayouts
{
public const string Application = "Application";
}

5
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/PageToolbarManager.cs

@ -3,16 +3,17 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars;
public class PageToolbarManager : IPageToolbarManager, ITransientDependency
{
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public PageToolbarManager(
IHybridServiceScopeFactory serviceScopeFactory)
IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
}

1
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs

@ -15,7 +15,6 @@ public class AbpAspNetCoreMultiTenancyModule : AbpModule
Configure<AbpTenantResolveOptions>(options =>
{
options.TenantResolvers.Add(new QueryStringTenantResolveContributor());
options.TenantResolvers.Add(new FormTenantResolveContributor());
options.TenantResolvers.Add(new RouteTenantResolveContributor());
options.TenantResolvers.Add(new HeaderTenantResolveContributor());
options.TenantResolvers.Add(new CookieTenantResolveContributor());

3
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/FormTenantResolveContributor.cs

@ -1,10 +1,11 @@
using System.Linq;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.AspNetCore.MultiTenancy;
[Obsolete("This may make some features of ASP NET Core unavailable, Will be removed in future versions.")]
public class FormTenantResolveContributor : HttpTenantResolveContributorBase
{
public const string ContributorName = "Form";

3
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

18
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj" />
</ItemGroup>
</Project>

29
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http.Json;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
[DependsOn(
typeof(AbpAspNetCoreMvcDaprModule),
typeof(AbpEventBusDaprModule)
)]
public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// TODO: Add NewtonsoftJson json converter.
Configure<JsonOptions>(options =>
{
options.SerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusOptions.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprEventBusOptions
{
public List<IAbpAspNetCoreMvcDaprPubSubProviderContributor> Contributors { get; }
public AbpAspNetCoreMvcDaprEventBusOptions()
{
Contributors = new List<IAbpAspNetCoreMvcDaprPubSubProviderContributor>();
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubConsts
{
public const string DaprSubscribeUrl = "dapr/subscribe";
public const string DaprEventCallbackUrl = "api/abp/dapr/event";
}

63
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProvider.cs

@ -0,0 +1,63 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProvider : ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpAspNetCoreMvcDaprEventBusOptions AspNetCoreMvcDaprEventBusOptions { get; }
protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
protected AbpDistributedEventBusOptions DistributedEventBusOptions { get; }
public AbpAspNetCoreMvcDaprPubSubProvider(
IServiceProvider serviceProvider,
IOptions<AbpAspNetCoreMvcDaprEventBusOptions> aspNetCoreDaprEventBusOptions,
IOptions<AbpDaprEventBusOptions> daprEventBusOptions,
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions)
{
ServiceProvider = serviceProvider;
AspNetCoreMvcDaprEventBusOptions = aspNetCoreDaprEventBusOptions.Value;
DaprEventBusOptions = daprEventBusOptions.Value;
DistributedEventBusOptions = distributedEventBusOptions.Value;
}
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> GetSubscriptionsAsync()
{
var subscriptions = new List<AbpAspNetCoreMvcDaprSubscriptionDefinition>();
foreach (var handler in DistributedEventBusOptions.Handlers)
{
foreach (var @interface in handler.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>)))
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
subscriptions.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinition()
{
PubSubName = DaprEventBusOptions.PubSubName,
Topic = eventName,
Route = AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl
});
}
}
if (AspNetCoreMvcDaprEventBusOptions.Contributors.Any())
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new AbpAspNetCoreMvcDaprPubSubProviderContributorContext(scope.ServiceProvider, subscriptions);
foreach (var contributor in AspNetCoreMvcDaprEventBusOptions.Contributors)
{
await contributor.ContributeAsync(context);
}
}
}
return subscriptions;
}
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubProviderContributorContext.cs

@ -0,0 +1,16 @@
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProviderContributorContext
{
public IServiceProvider ServiceProvider { get; }
public List<AbpAspNetCoreMvcDaprSubscriptionDefinition> Subscriptions { get; }
public AbpAspNetCoreMvcDaprPubSubProviderContributorContext(IServiceProvider serviceProvider, List<AbpAspNetCoreMvcDaprSubscriptionDefinition> daprSubscriptionModels)
{
ServiceProvider = serviceProvider;
Subscriptions = daprSubscriptionModels;
}
}

36
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs

@ -0,0 +1,36 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
[Area("abp")]
[RemoteService(Name = "abp")]
public class AbpAspNetCoreMvcDaprPubSubController : AbpController
{
[HttpGet(AbpAspNetCoreMvcDaprPubSubConsts.DaprSubscribeUrl)]
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> SubscribeAsync()
{
return await HttpContext.RequestServices.GetRequiredService<AbpAspNetCoreMvcDaprPubSubProvider>().GetSubscriptionsAsync();
}
[HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
public virtual async Task<IActionResult> EventsAsync()
{
var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body);
var request = JsonSerializer.Deserialize<AbpAspNetCoreMvcDaprSubscriptionRequest>(bodyJsonDocument.RootElement.GetRawText(),
HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions);
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
var daprSerializer = HttpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var eventData = daprSerializer.Deserialize(bodyJsonDocument.RootElement.GetProperty("data").GetRawText(), distributedEventBus.GetEventType(request.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
return Ok();
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/IAbpAspNetCoreMvcDaprPubSubProviderContributor.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public interface IAbpAspNetCoreMvcDaprPubSubProviderContributor
{
Task ContributeAsync(AbpAspNetCoreMvcDaprPubSubProviderContributorContext context);
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionDefinition.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionDefinition
{
public string PubSubName { get; set; }
public string Topic { get; set; }
public string Route { get; set; }
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionRequest
{
public string PubSubName { get; set; }
public string Topic { get; set; }
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy.cs

@ -0,0 +1,11 @@
using System.Text.Json;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToLower();
}
}

25
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs

@ -0,0 +1,25 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter : JsonConverter<AbpAspNetCoreMvcDaprSubscriptionDefinition>
{
private JsonSerializerOptions _writeJsonSerializerOptions;
public override AbpAspNetCoreMvcDaprSubscriptionDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, AbpAspNetCoreMvcDaprSubscriptionDefinition value, JsonSerializerOptions options)
{
_writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(new JsonSerializerOptions(options)
{
PropertyNamingPolicy = new AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy()
}, x => x == this);
JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions);
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

22
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo.Abp.AspNetCore.Mvc.Dapr.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.AspNetCore" Version="1.8.0" />
</ItemGroup>
</Project>

13
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/AbpAspNetCoreMvcDaprModule.cs

@ -0,0 +1,13 @@
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr;
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpDaprModule)
)]
public class AbpAspNetCoreMvcDaprModule : AbpModule
{
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSize.cs

@ -5,5 +5,11 @@ public enum AbpModalSize
Default,
Small,
Large,
ExtraLarge
ExtraLarge,
Fullscreen,
FullscreenSmDown,
FullscreenMdDown,
FullscreenLgDown,
FullscreenXlDown,
FullscreenXxlDown
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalSizeExtensions.cs

@ -14,6 +14,18 @@ public static class AbpModalSizeExtensions
return "modal-xl";
case AbpModalSize.Default:
return "";
case AbpModalSize.Fullscreen:
return "modal-fullscreen";
case AbpModalSize.FullscreenSmDown:
return "modal-fullscreen-sm-down";
case AbpModalSize.FullscreenMdDown:
return "modal-fullscreen-md-down";
case AbpModalSize.FullscreenLgDown:
return "modal-fullscreen-lg-down";
case AbpModalSize.FullscreenXlDown:
return "modal-fullscreen-xl-down";
case AbpModalSize.FullscreenXxlDown:
return "modal-fullscreen-xxl-down";
default:
throw new AbpException($"Unknown {nameof(AbpModalSize)}: {size}");
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarManager.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@ -10,11 +11,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars;
public class PageToolbarManager : IPageToolbarManager, ITransientDependency
{
protected AbpPageToolbarOptions Options { get; }
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public PageToolbarManager(
IOptions<AbpPageToolbarOptions> options,
IHybridServiceScopeFactory serviceScopeFactory)
IServiceScopeFactory serviceScopeFactory)
{
Options = options.Value;
ServiceScopeFactory = serviceScopeFactory;

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/Default.cshtml

@ -1,5 +1,4 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook
@model LayoutHookViewModel
@model Volo.Abp.Ui.LayoutHooks.LayoutHookViewModel
@if (Model.Hooks.Any())
{
foreach (var hook in Model.Hooks)

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHookViewComponent.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.Ui.LayoutHooks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook;

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

@ -171,7 +171,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var policyName in policyNames)
{
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null && _permissionDefinitionManager.GetOrNull(policyName) != null)
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null &&
await _permissionDefinitionManager.GetOrNullAsync(policyName) != null)
{
abpPolicyNames.Add(policyName);
}
@ -214,9 +215,9 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{
var dictionary = new Dictionary<string, string>();
var localizer = _serviceProvider.GetRequiredService(
var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService(
typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType)
) as IStringLocalizer;
);
foreach (var localizedString in localizer.GetAllStrings())
{

2
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Hosting/AbpHostingEnvironmentExtensions.cs

@ -9,7 +9,7 @@ public static class AbpHostingEnvironmentExtensions
this IWebHostEnvironment env,
AbpConfigurationBuilderOptions options = null)
{
options = options ?? new AbpConfigurationBuilderOptions();
options ??= new AbpConfigurationBuilderOptions();
if (options.BasePath.IsNullOrEmpty())
{

52
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/DependencyInjection/HttpContextServiceScopeFactory.cs

@ -1,52 +0,0 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.DependencyInjection;
[ExposeServices(
typeof(IHybridServiceScopeFactory),
typeof(HttpContextServiceScopeFactory)
)]
[Dependency(ReplaceServices = true)]
public class HttpContextServiceScopeFactory : IHybridServiceScopeFactory, ITransientDependency
{
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
public HttpContextServiceScopeFactory(
IHttpContextAccessor httpContextAccessor,
IServiceScopeFactory serviceScopeFactory)
{
HttpContextAccessor = httpContextAccessor;
ServiceScopeFactory = serviceScopeFactory;
}
public virtual IServiceScope CreateScope()
{
var httpContext = HttpContextAccessor.HttpContext;
if (httpContext == null)
{
return ServiceScopeFactory.CreateScope();
}
return new NonDisposedHttpContextServiceScope(httpContext.RequestServices);
}
protected class NonDisposedHttpContextServiceScope : IServiceScope
{
public IServiceProvider ServiceProvider { get; }
public NonDisposedHttpContextServiceScope(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Dispose()
{
}
}
}

4
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IDeletionAuditedObject.cs

@ -10,7 +10,7 @@ public interface IDeletionAuditedObject : IHasDeletionTime
/// <summary>
/// Id of the deleter user.
/// </summary>
Guid? DeleterId { get; set; }
Guid? DeleterId { get; }
}
/// <summary>
@ -22,5 +22,5 @@ public interface IDeletionAuditedObject<TUser> : IDeletionAuditedObject
/// <summary>
/// Reference to the deleter user.
/// </summary>
TUser Deleter { get; set; }
TUser Deleter { get; }
}

2
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasDeletionTime.cs

@ -11,5 +11,5 @@ public interface IHasDeletionTime : ISoftDelete
/// <summary>
/// Deletion time.
/// </summary>
DateTime? DeletionTime { get; set; }
DateTime? DeletionTime { get; }
}

2
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IHasModificationTime.cs

@ -10,5 +10,5 @@ public interface IHasModificationTime
/// <summary>
/// The last modified time for this entity.
/// </summary>
DateTime? LastModificationTime { get; set; }
DateTime? LastModificationTime { get; }
}

4
framework/src/Volo.Abp.Auditing.Contracts/Volo/Abp/Auditing/IModificationAuditedObject.cs

@ -10,7 +10,7 @@ public interface IModificationAuditedObject : IHasModificationTime
/// <summary>
/// Last modifier user for this entity.
/// </summary>
Guid? LastModifierId { get; set; }
Guid? LastModifierId { get; }
}
/// <summary>
@ -22,5 +22,5 @@ public interface IModificationAuditedObject<TUser> : IModificationAuditedObject
/// <summary>
/// Reference to the last modifier user of this entity.
/// </summary>
TUser LastModifier { get; set; }
TUser LastModifier { get; }
}

17
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
@ -24,4 +26,17 @@ public class AbpAuditingModule : AbpModule
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var applicationName = context.Services.GetApplicationName();
if (!applicationName.IsNullOrEmpty())
{
Configure<AbpAuditingOptions>(options =>
{
options.ApplicationName = applicationName;
});
}
}
}

16
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs

@ -99,7 +99,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
{
if (targetObject is IHasModificationTime objectWithModificationTime)
{
objectWithModificationTime.LastModificationTime = Clock.Now;
ObjectHelper.TrySetProperty(objectWithModificationTime, x => x.LastModificationTime, () => Clock.Now);
}
}
@ -112,7 +112,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
if (!CurrentUser.Id.HasValue)
{
modificationAuditedObject.LastModifierId = null;
ObjectHelper.TrySetProperty(modificationAuditedObject, x => x.LastModifierId, () => null);
return;
}
@ -120,7 +120,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
{
if (multiTenantEntity.TenantId != CurrentUser.TenantId)
{
modificationAuditedObject.LastModifierId = null;
ObjectHelper.TrySetProperty(modificationAuditedObject, x => x.LastModifierId, () => null);
return;
}
}
@ -134,7 +134,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
}
*/
modificationAuditedObject.LastModifierId = CurrentUser.Id;
ObjectHelper.TrySetProperty(modificationAuditedObject, x => x.LastModifierId, () => CurrentUser.Id);
}
protected virtual void SetDeletionTime(object targetObject)
@ -143,7 +143,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
{
if (objectWithDeletionTime.DeletionTime == null)
{
objectWithDeletionTime.DeletionTime = Clock.Now;
ObjectHelper.TrySetProperty(objectWithDeletionTime, x => x.DeletionTime, () => Clock.Now);
}
}
}
@ -162,7 +162,7 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
if (!CurrentUser.Id.HasValue)
{
deletionAuditedObject.DeleterId = null;
ObjectHelper.TrySetProperty(deletionAuditedObject, x => x.DeleterId, () => null);
return;
}
@ -170,11 +170,11 @@ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
{
if (multiTenantEntity.TenantId != CurrentUser.TenantId)
{
deletionAuditedObject.DeleterId = null;
ObjectHelper.TrySetProperty(deletionAuditedObject, x => x.DeleterId, () => null);
return;
}
}
deletionAuditedObject.DeleterId = CurrentUser.Id;
ObjectHelper.TrySetProperty(deletionAuditedObject, x => x.DeleterId, () => CurrentUser.Id);
}
}

14
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs

@ -0,0 +1,14 @@
using JetBrains.Annotations;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
public interface ICanAddChildPermission
{
PermissionDefinition AddPermission(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true);
}

3
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs

@ -37,8 +37,7 @@ public interface IPermissionDefinitionContext
/// </summary>
PermissionGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both);
ILocalizableString displayName = null);
/// <summary>
/// Tries to remove a permission group.

13
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs

@ -1,17 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Authorization.Permissions;
public interface IPermissionDefinitionManager
{
[NotNull]
PermissionDefinition Get([NotNull] string name);
[ItemNotNull]
Task<PermissionDefinition> GetAsync([NotNull] string name);
[CanBeNull]
PermissionDefinition GetOrNull([NotNull] string name);
[ItemCanBeNull]
Task<PermissionDefinition> GetOrNullAsync([NotNull] string name);
IReadOnlyList<PermissionDefinition> GetPermissions();
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
IReadOnlyList<PermissionGroupDefinition> GetGroups();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

14
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

@ -7,7 +7,9 @@ using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition>
public class PermissionDefinition :
IHasSimpleStateCheckers<PermissionDefinition>,
ICanAddChildPermission
{
/// <summary>
/// Unique name of the permission.
@ -110,6 +112,16 @@ public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition
return child;
}
PermissionDefinition ICanAddChildPermission.AddPermission(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)
{
return this.AddChild(name, displayName, multiTenancySide, isEnabled);
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.

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

Loading…
Cancel
Save