Browse Source

Merge branch 'dev' into maliming/identity-pro-features-settings

pull/7719/head
maliming 6 years ago
parent
commit
12f7b98744
  1. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
  3. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  4. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  5. 248
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  6. 257
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  7. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  8. 20
      docs/en/Background-Jobs-RabbitMq.md
  9. 22
      docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
  10. 2
      docs/en/UI/Angular/Router-Events.md
  11. 4
      docs/en/UI/Angular/Service-Proxies.md
  12. 154
      docs/zh-Hans/Background-Jobs-RabbitMq.md
  13. 20
      docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
  14. 2
      docs/zh-Hans/Repositories.md
  15. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs
  16. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarItem.cs
  17. 31
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs
  18. 34
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
  19. 17
      framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
  20. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs
  21. 76
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs
  22. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
  23. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs
  24. 78
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs
  25. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs
  26. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs
  27. 11
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs
  28. 54
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs
  29. 14
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs
  30. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
  31. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs
  32. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs
  33. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs
  34. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
  35. 59
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs
  36. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  37. 12
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs
  38. 21
      framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs
  39. 21
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  40. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
  41. 26
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs
  42. 24
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLogger.cs
  43. 16
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLogger.cs
  44. 16
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModuleHelper.cs
  45. 7
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs
  46. 12
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs
  47. 5
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceExtensions.cs
  48. 5
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/PlugIns/PlugInSourceList.cs
  49. 2
      framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj
  50. 4
      framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs
  51. 6
      framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDatabaseInfo.cs
  52. 15
      framework/src/Volo.Abp.Data/Volo/Abp/Data/ApplyDatabaseMigrationsEto.cs
  53. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  54. 32
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs
  55. 22
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs
  56. 42
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs
  57. 11
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs
  58. 7
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs
  59. 58
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs
  60. 28
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Permissions/FakePermissionStore.cs
  61. 16
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Permissions/TestPermissionDefinitionProvider.cs
  62. 129
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Toolbars/Toolbar_Tests.cs
  63. 8
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Modularity/ModuleLoader_Tests.cs
  64. 2
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj
  65. 21
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs
  66. 28
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/FakePermissionStore.cs
  67. 60
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs
  68. 24
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/TestPermissionDefinitionProvider.cs
  69. 17
      modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs
  70. 128
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  71. 2007
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210226112040_BlogPost_Author.Designer.cs
  72. 109
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210226112040_BlogPost_Author.cs
  73. 2012
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.Designer.cs
  74. 25
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302064229_Added_EntityType_to_Media.cs
  75. 2016
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.Designer.cs
  76. 24
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302075529_Added_Content_To_BlogPost.cs
  77. 2020
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.Designer.cs
  78. 24
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210302091451_Added_Content_To_Page.cs
  79. 42
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  80. 6
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  81. 3
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json
  82. 21
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/LICENSE
  83. 90
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/README.md
  84. 40
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/package.json
  85. 23
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/slugify.d.ts
  86. 66
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/slugify.js
  87. 5
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock
  88. 5
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs
  89. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs
  90. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs
  91. 23
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentCreateDto.cs
  92. 15
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentDto.cs
  93. 13
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs
  94. 10
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListInput.cs
  95. 15
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentUpdateDto.cs
  96. 19
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs
  97. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/CreateMediaInputStream.cs
  98. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/CreatePageInputDto.cs
  99. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs
  100. 3
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/UpdatePageInputDto.cs

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

@ -91,6 +91,7 @@
"UserNotFound": "User not found", "UserNotFound": "User not found",
"{0}WillBeRemovedFromDevelopers": "{0} Will be removed from developers, do you confirm?", "{0}WillBeRemovedFromDevelopers": "{0} Will be removed from developers, do you confirm?",
"{0}WillBeRemovedFromOwners": "{0} Will be removed from owners, do you confirm?", "{0}WillBeRemovedFromOwners": "{0} Will be removed from owners, do you confirm?",
"{0}WillBeRemovedFromMembers": "{0} Will be removed from members, do you confirm?",
"Computers": "Computers", "Computers": "Computers",
"UniqueComputerId": "Unique computer id", "UniqueComputerId": "Unique computer id",
"LastSeenDate": "Last seen date", "LastSeenDate": "Last seen date",
@ -213,7 +214,9 @@
"Currency": "Currency", "Currency": "Currency",
"Gateway": "Gateway", "Gateway": "Gateway",
"State": "State", "State": "State",
"FailReason": "Fail reason" "FailReason": "Fail reason",
"ReIndexAllPosts": "ReIndex All Posts",
"ReIndexAllPostsConfirmationMessage": "Are you sure to reindex all posts?",
"SuccessfullyReIndexAllPosts": "Successfully reindex for all posts"
} }
} }

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

@ -163,6 +163,9 @@
"Enum:ContentSource:2": "Video İçerik", "Enum:ContentSource:2": "Video İçerik",
"DeleteCoverImage": "Kapak Fotoğrafını Sil", "DeleteCoverImage": "Kapak Fotoğrafını Sil",
"DeleteCoverImageConfirmationMessage": "Kapak fotoğrafını \"{0}\" isimli makale için silmek istediğinize emin misiniz?", "DeleteCoverImageConfirmationMessage": "Kapak fotoğrafını \"{0}\" isimli makale için silmek istediğinize emin misiniz?",
"DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir" "DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir",
"ReIndexAllPosts": "Yeniden indeksle",
"ReIndexAllPostsConfirmationMessage": "Tüm gönderileri yeniden indeksleyeceğinizden emin misiniz?",
"SuccessfullyReIndexAllPosts": "Tüm gönderiler başarıyla yeniden indekslendi"
} }
} }

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

@ -30,6 +30,8 @@
"Permission:License": "License", "Permission:License": "License",
"Permission:UserInfo": "Usere info", "Permission:UserInfo": "Usere info",
"SeeDocuments": "See Documents", "SeeDocuments": "See Documents",
"Samples": "Samples" "Samples": "Samples",
"Framework": "Framework",
"Support": "Support"
} }
} }

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

@ -30,6 +30,8 @@
"Permission:License": "许可", "Permission:License": "许可",
"Permission:UserInfo": "用户信息", "Permission:UserInfo": "用户信息",
"SeeDocuments": "查看文档", "SeeDocuments": "查看文档",
"Samples": "示例" "Samples": "示例",
"Framework": "框架",
"Support": "支持"
} }
} }

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

@ -41,6 +41,250 @@
"Unsubscribe": "Unsubscribe", "Unsubscribe": "Unsubscribe",
"NotOrganizationMember": "You are not member of any organization.", "NotOrganizationMember": "You are not member of any organization.",
"UnsubscribeLicenseExpirationEmailSuccessTitle": "Successfully unsubscribed", "UnsubscribeLicenseExpirationEmailSuccessTitle": "Successfully unsubscribed",
"UnsubscribeLicenseExpirationEmailSuccessMessage": "You will not receive license expiration date reminder emails anymore." "UnsubscribeLicenseExpirationEmailSuccessMessage": "You will not receive license expiration date reminder emails anymore.",
} "IndexPageHeroSection": "<span class=\"first-line\">A complete</span><span class=\"second-line\"> web development platform</span><span class=\"third-line\">built-on <img src=\"{0}\" width=\"110\" class=\"ml-1\" /> framework</span>",
"AbpCommercialShortDescription": "ABP Commercial provides pre-built application modules, rapid application development tooling, professional UI themes, premium support and more.",
"LiveDemo": "Live Demo",
"GetLicence" :"Get a Licence",
"Application": "Application",
"StartupTemplates": "Startup Templates",
"Startup": "Startup",
"Templates": "Templates",
"Developer": "Developer",
"Tools": "Tools",
"Premium": "Premium",
"PremiumSupport": "Premium Support",
"PremiumForumSupport": "Premium Forum Support",
"UI": "UI",
"Themes": "Themes",
"JoinOurNewsletter": "Join Our Newsletter",
"Send": "Send",
"Learn": "Learn",
"AdditionalServices": "Additional Services",
"WhatIsABPFramework": "WHAT IS THE ABP FRAMEWORK?",
"OpenSourceBaseFramework": "Open Source Base Framework",
"ABPFrameworkExplanation": "<p class=\"lead\">ABP Commercial is based on the ABP Framework, an open source and community driven web application framework for ASP.NET Core.</p><p>ABP Framework provides an excellent infrastructure to write maintainable, extensible and testable code with best practices.</p><p>Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.</p>",
"Modular": "Modular",
"MicroserviceCompatible": "Microservice compatible",
"DomainDrivenDesignInfrastructure": "Domain Driven Design Infrastructure",
"MultiTenancy": "Multi-Tenancy",
"DistributedMessaging": "Distributed Messaging",
"DynamicProxying": "Dynamic Proxying",
"BackgroundJobs": "Background Jobs",
"AuditLogging": "Audit Logging",
"BLOBStoring": "BLOB Storing",
"BundlingMinification": "Bundling & Minification",
"AdvancedLocalization": "Advanced Localization",
"ManyMore": "Many more",
"ExploreTheABPFramework": "Explore the ABP Framework",
"WhyUseTheABPCommercial": "Why Use The ABP Commercial?",
"WhyUseTheABPCommercialExplanation": "<p class=\"lead mt- 5\">Building enterprise-grade web applications can be complex and time-consuming.</p><p>ABP Commercial offers the perfect base infrastructure necessary for all modern enterprise-grade ASP.NET Core based solutions. Right from the design to deployment, the entire development cycle is empowered by ABP's built-in features & modules.</p>",
"StartupTemplatesShortDescription": "Startup templates make you jump-start to your project in a few seconds.",
"UIFrameworksOptions": "UI frameworks options;",
"DatabaseProviderOptions": "Database provider options;",
"PreBuiltApplicationModules": "Pre-Built Application Modules",
"PreBuiltApplicationModulesShortDescription": "Most common application requirements are already developed for you as reusable modules.",
"Account": "Account",
"Blogging": "Blogging",
"Identity": "Identity",
"IdentityServer": "Identity Server",
"Saas": "Saas",
"LanguageManagement": "Language Management",
"TextTemplateManagement": "Text Template Management",
"See All Modules": "SeeAllModules",
"ABPSuite": "ABP Suite",
"AbpSuiteShortDescription": "ABP Suite is a complementary tool to the ABP Commercial.",
"AbpSuiteExplanation": "It allows you to build web pages in a matter of minutes. It's a .NET Core Global tool which can be installed from the command line. It can create a new ABP solution, generate CRUD pages from the database to the front-end.",
"Details": "Details",
"LeptonTheme": "Lepton Theme",
"ProfessionalModernUIThemes": "Professional, modern UI themes",
"LeptonThemeExplanation": "Lepton provides a gamut of Bootstrap admin themes that serve as a solid foundation for any project requiring admin dashboard.",
"DefaultTheme": "Default Theme",
"MaterialTheme": "Material Theme",
"Default2Theme": "Default 2 Theme",
"DarkTheme": "Dark Theme",
"DarkBlueTheme": "Dark Blue Theme",
"LightTheme": "Light Theme",
"ProudToWorkWith": "Proud to Work With",
"OurConsumers": "Hundreds of enterprises and developers over 50 countries worldwide rely on ABP Commercial.",
"JoinOurConsumers": "Join them and build amazing products fast.",
"AdditionalServicesExplanation": "Do you need additional or custom services? <strong>We and our partners can provide;</strong>",
"CustomProjectDevelopment": "Custom Project Development",
"CustomProjectDevelopmentExplanation": "Dedicated developers for your custom projects.",
"PortingExistingProjects": "Porting Existing Projects",
"PortingExistingProjectsExplanation": "Migrating your legacy projects to the ABP platform.",
"LiveSupport": "Live Support",
"LiveSupportExplanation": "Live remote support option when you need it.",
"Training": "Training",
"TrainingExplanation": "Dedicated training for your developers.",
"OnBoarding": "Onboarding",
"OnBoardingExplanation": "Help to setup your development, CI & CD environments.",
"PrioritizedTechnicalSupport": "Prioritized Technical Support",
"PremiumSupportExplanation": "Beside the great community support of the ABP framework, our support team answers technical questions and problems of the commercial users with high priority.",
"SeeTheSupportOptions": "See the Support Options",
"Contact": "Contact",
"TellUsWhatYouNeed": "Tell us what you need.",
"YourMessage": "Your Message",
"YourFullName": "Your full name",
"EmailField": "E-mail Address",
"YourEmailAddress": "Your e-mail address",
"HowMayWeHelpYou": "How may we help you",
"SendMessage": "Send Message",
"Success": "Success",
"WeWillReplyYou.": "Your message is sent! We will reply you in a short time.",
"GoHome": "Go Home",
"CreateLiveDemo": "Create Live Demo",
"RegisterToTheNewsletter": "Register to the newsletter to get information on happenings about ABP.IO, like new releases.",
"EnterYourEmailOrLogin": "Enter your e-mail address to create your demo or <a href=\"{0}\">Login</a> using your existing account.",
"ApplicationTemplate": "Application Template",
"ApplicationTemplateExplanation": "Application startup template is used to create a new web application.",
"EfCoreProvider": "Entity Framework (Supports SQL Server, MySQL, PostgreSQL, Oracle <a href=\"https://docs.microsoft.com/en-us/ef/core/providers/\">and others</a>)",
"AlreadyIncludedInTemplateModules": "Following modules are already included and configured in this template:",
"ApplicationTemplateArchitecture": "This application template also supports tiered architecture where UI layer, API layer and the authentication service are physically separated.",
"SeeTheGuideOrGoToTheLiveDemo": "See the developer guide for technical information about this template or go to the live demo.",
"DeveloperGuide": "Developer Guide",
"ModuleTemplate": "Module Template",
"ModuleTemplateExplanation1": "You want to create a module and reuse across different applications? This startup template prepares everything to start to create a reusable <strong>application module</strong> or a <strong>microservice</strong>.",
"ModuleTemplateExplanation2": "<p>You can support single or multiple UI frameworks, single or multiple database providers for a single module. The startup template is configured to run and <strong>test your module</strong> in a minimal application in addition to the unit and integration test infrastructure. </p> <p>See the developer guide for technical information about this template.</p>",
"WithAllStyleOptions": "with all style options",
"Demo": "Demo",
"SeeAllModules": "See All Modules",
"ABPCLIExplanation": "ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions.",
"ABPSuiteEasilyCURD": "ABP Suite is a tool which allows you to easily create CRUD pages",
"WeAreHereToHelp": "We are Here to <span class=\"zero-text\">Help</span>",
"BrowseOrAskQuestion": "You can browse our help topics or search in frequently asked questions, or you can ask us a question by using the <a href=\"{0}\" class=\"text-success\">contact form</a>.",
"SearchQuestionPlaceholder": "Search in frequently asked questions",
"WhatIsTheABPCommercial": "What is the ABP Commercial?",
"WhatAreDifferencesThanAbpFramework": "What are the differences between the open source ABP Framework and the ABP Commercial?",
"ABPCommercialExplanation": "ABP Commercial is a set of premium modules, tools, themes and services built on top of the open source <a target=\"_blank\" href=\"{0}\">ABP framework</a>. ABP Commercial is being developed and supported by the same team behind the ABP framework.",
"WhatAreDifferencesThanABPFrameworkExplanation": "<p> <a target=\"_blank\" href=\"{0}\">ABP framework</a> is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to make you focusing on your own business code rather than repeating yourself for every new project. It is based on software development best practices and popular tools you already know. </p> <p> ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).</p>",
"VisitTheFrameworkVSCommercialDocument": "Visit the following link, for more information <a href=\"{0}\" target=\"_blank\"> {1} </a>",
"ABPCommercialFollowingBenefits": "ABP Commercial adds the following benefits on top of the ABP framework;",
"Professional": "Professional",
"UIThemes": "UI themes",
"EnterpriseModules": "Enterprise ready, feature rich, pre-built <a href=\"{0}\">application modules</a> (e.g. Identity Server management, SaaS management, language management)",
"ToolingToSupport": "Tooling to support your development productivity (e.g. <a href=\"{0}\">ABP Suite</a>)",
"PremiumSupportLink": "Premium <a href=\"{0}\" target=\"_blank\">support</a>",
"WhatDoIDownloadABPCommercial": "What do I download when I purchase the ABP Commercial?",
"CreateUnlimitedSolutions": "Once you purchase an ABP Commercial license, you will be able to create unlimited solutions like described in the <a href=\"{0}\">Getting Started</a> document.",
"ABPCommercialSolutionExplanation": "When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes are used a NuGet/NPM packages by default.",
"StartDevelopWithTutorials": "The downloaded solution is well architected and documented. You can start to develop your own business code based on it following <a href=\"{0}\">the tutorials</a>",
"TryTheCommercialDemo": "You can try <a href=\"{0}\">the demo</a> to see a sample application created using the ABP Commercial startup template.",
"HowManyProducts": "How many different products/solutions can I build using the ABP Commercial?",
"HowManyProductsExplanation": "There is no limit to create an ABP project. You can create as many project as you want, develop and upload them to different servers.",
"HowManyDevelopers": "How many developers can work on the ABP Commercial?",
"HowManyDevelopersExplanation": "ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. See <a href=\"{0}\">the prices</a> page for license types, developer limits and additional developer costs.",
"ChangingLicenseType": "Can I change my license type in the future?",
"ChangingLicenseTypeExplanation": "You can always add new developers in your same license type. See also \"How many developers can work on the ABP Commercial?\". You can also upgrade to a higher license by paying the calculated price difference. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date.",
"LicenseExtendUpgradeDiff": "What is the difference between license extend and upgrade?",
"LicenseExtendUpgradeDiffExplanation": "<strong>Extending:</strong> By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.<hr/><strong>Upgrading:</strong> By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the <a href=\"/pricing\">license comparison table</a> to check the differences between the license plans.<strong>On the other hand, when you upgrade, your license expiry date will not change!</strong>To extend your license end date, you need to extend your license.",
"LicenseRenewalCost": "What is the license renewal cost after 1 year?",
"LicenseRenewalCostExplanation": "You can renew (extend) your license by paying %80 of the current license price. For example; Standard ABP Team License is <span class=\"text-muted\">$</span>{0} and renewal price of the Team License is <span class=\"text-muted\">$</span>{1}.",
"IsSourceCodeIncluded": "Does my license include the source code of the commercial modules and themes?",
"IsSourceCodeIncludedExplanation1": "Depends on the license type you've purchased:",
"IsSourceCodeIncludedExplanation2": "<strong>Team</strong>: Your solution uses the modules and the themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of the modules and the themes.",
"IsSourceCodeIncludedExplanation3": "<strong>Business/Enterprise</strong>: In addition to the Team license, you are able to download the source code of any module or theme you need. You can even remove the NuGet/NPM package references for a particular module and add its source code directly to your solution to fully change it.",
"IsSourceCodeIncludedExplanation4": "<p>Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, then it will not be possible to automatically upgrade the module when a new version is released.</p><p>None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assist to your development.</p><p>See <a href=\"{0}\">the pricing</a> page for other differences between the license types.</p>",
"ChangingDevelopers": "Can I change the registered developers of my organization in the future?",
"ChangingDevelopersExplanation": "In addition to add new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.",
"WhatHappensWhenLicenseEnds": "What happens when my license period ends?",
"WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial has <a href=\"{0}\" target=\"_blank\">perpetual license type</a>.After your license expires, you can continue developing your project. And you are not obliged to renew your license. After your license expires;",
"WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue to develop your existing applications forever.",
"WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version. For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x)",
"WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.",
"WhatHappensWhenLicenseEndsExplanation5": "You can not use the ABP Suite.",
"WhatHappensWhenLicenseEndsExplanation6": "You can not get the <a href=\"{0}\">premium support</a> anymore.",
"WhatHappensWhenLicenseEndsExplanation7": "You can renew your subscription if you want to continue to get these benefits. There is a 20% discount when you renew your subscription.",
"WhenShouldIRenewMyLicense": "When should I renew my license?",
"WhenShouldIRenewMyLicenseExplanation1": "If you renew your license within <strong>1 month</strong> after your license expires, %20 discount will be applied to total license price.",
"WhenShouldIRenewMyLicenseExplanation2": "If you renew your license after 1 month from your license expire date, the renew price will be same as license purchase price and there will be no discount for your renewal.",
"TrialPlan": "Do you have a trial plan?",
"TrialPlanExplanation": "For now, ABP Commercial doesn't have a trial plan. For the Team licenses we provide 30 days money back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.",
"DoYouAcceptBankWireTransfer": "Do you accept bank wire transfer?",
"DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfer.<br />After sending the license amount via bank wire transfer, send us your receipt and the requested license type via e-mail.<br />Our international bank account information:",
"HowToUpgrade": "How to upgrade existing applications when a new version is available?",
"HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and the theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.",
"HowToUpgradeExplanation2": "In addition to the standard NuGet/NPM upgrades, <a href=\"{0}\">ABP CLI</a> provides an update command that automatically finds and upgrades all ABP related packages in your solution.",
"DatabaseSupport": "Which database systems are supported?",
"DatabaseSupportExplanation": "ABP Framework itself is database agnostic and can work with any database provider by its nature. See <a href=\"{0}\" target=\"_blank\">the data access document</a> for a list of currently implemented providers.",
"UISupport": "Which UI frameworks are supported?",
"Supported": "Supported",
"UISupportExplanation": "ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. See <a href=\"{0}\">the getting started document</a> for the up-to-date list of UI options.",
"MicroserviceSupport": "Does it support the micro-service architecture?",
"MicroserviceSupportExplanation1": "One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. See the <a href=\"{0}\">micro-service architecture</a> document to understand how it helps to create micro-service systems.",
"MicroserviceSupportExplanation2": "All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the <a href=\"{0}\">module development best practices</a> document.",
"MicroserviceSupportExplanation3": "We provide a sample <a href=\"{0}\">micro-service demo solution</a>that demonstrates a micro-service architecture implementation to help you to create your own solution.",
"MicroserviceSupportExplanation4": "So, the short answer is \"<strong>yes, it supports micro-service architecture</strong>\".",
"MicroserviceSupportExplanation5": "However, a micro-service system is a solution and every solution will have different requirements, network topology, communication scenarios, authentication possibilities, database separation/sharing decisions, runtime configurations, 3rd party system integrations and many more.",
"MicroserviceSupportExplanation6": "The ABP Framework and the ABP Commercial provides infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you to build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring some parts together based on your requirements.",
"WhereCanIDownloadSourceCode": "Where can I download source-code?",
"WhereCanIDownloadSourceCodeExplanation": "You can download the source code of all ABP modules, Angular packages and themes via ABP Suite or ABP CLI. See <a href=\"{0}\">How to download source-code?</a>",
"ComputerLimitation": "How many computers can a developer login when developing ABP?",
"ComputerLimitationExplanation": "We specifically permit <strong>{0} computers</strong> per individual/licensed developer. Whenever there is a need for a developer to develop ABP Commercial products on a third machine, an e-mail should be sent to license@abp.io explaining the situation and we will then make the appropriate allocation in our system.",
"RefundPolicy": "Do you have a refund policy?",
"RefundPolicyExplanation": "You can request a refund within <strong>30 days</strong> of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (and any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.",
"HowCanIRefundVat": "How can I refund VAT?",
"HowCanIRefundVatExplanation1": "If you made the payment using 2Checkout, you can refund VAT via your 2Checkout account:",
"HowCanIRefundVatExplanation2": "Log in into your <a href=\"https://secure.2checkout.com/cpanel/login.php\" target=\"_blank\">2Checkout</a> account",
"HowCanIRefundVatExplanation3": "Find the appropriate order and press \"Refund Belated VAT\" (enter your VAT ID)",
"HowCanIGetMyInvoice": "How can I get my invoice?",
"HowCanIGetMyInvoiceExplanation": "There are 2 payment gateways for purchasing a license: PayU and 2Checkout. If you purchase your license through 2Checkout gateway, it sends the PDF invoice to your email address, see <a href=\"https://knowledgecenter.2checkout.com/Documentation/03Billing-and-payments/Payment-operations/How-do-invoices-work\">2Checkout invoicing.</a> If you purchase through the PayU gateway or via bank wire transfer, we will prepare and send your invoice. You can request your invoice from the <a href=\"{0}\">organization management page.</a>",
"Forum": "Forum",
"SupportExplanation": "ABP Commercial licenses provides a premium forum support by a team consists of the ABP Framework experts.",
"PrivateTicket": "Private Ticket",
"PrivateTicketExplanation": "Enterprise License also includes a private support with e-mail and ticket system.",
"AbpSuiteExplanation1": "ABP Suite allows you to build web pages in a matter of minutes. It's a .NET Core Global tool that can be installed from the command line.",
"AbpSuiteExplanation2": "It can create a new ABP solution, generate CRUD pages from the database to the front-end. For technical overview see <a href=\"{0}\">the document</a>",
"FastEasy": "Fast & Easy",
"AbpSuiteExplanation3": "ABP Suite allows you to easily create CRUD pages. You just need to define your entity and its properties, let the rest to ABP Suite for you! ABP Suite generates all the necessary code for your CRUD page in a few seconds. It supports Angular, MVC and Blazor user interfaces.",
"RichOptions": "Rich Options",
"AbpSuiteExplanation4": "ABP Suite supports multiple UI options like <a href=\"https://docs.microsoft.com/en-us/aspnet/core/razor-pages\">Razor Pages</a> and <a href=\"https://angular.io\">Angular</a>.It also supports multiple databases like <a href=\"https://www.mongodb.com\">MongoDB</a> and all databases supported by <strong>EntityFramework Core</strong> (MS SQL Server, Oracle, MySql, PostgreSQL and <a href=\"https://docs.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli\">more</a>).",
"AbpSuiteExplanation5": "Good thing is that, you don't have to worry about those options. ABP Suite understands your project type and generates the code for your project and places the generated code in correct place in your project.",
"SourceCode": "Source Code",
"AbpSuiteExplanation6": "ABP Suite generates the source code for you! It doesn't generate magic files to generate the web page. ABP Suite generates the source code for <strong>Entity, Repository, Application Service, Code First Migration, JavaScript/TypeScript and CSHTML/HTML</strong> and necessary Interfaces as well. ABP Suite also generates the code according to <strong>Best Practices</strong> of software development, so you don't have to worry about generated code's quality.",
"AbpSuiteExplanation7": "Since you have the source code of building blocks of the generated CRUD page in correct application layers, you can easily modify the source code and inject your custom/bussiness logic to the generated code.",
"CrossPlatform": "Cross Platform",
"AbpSuiteExplanation8": "ABP Suite is built with .NET Core and it is cross platform. It runs as a web application on your local computer. You can run it on <strong>Windows</strong>, <strong>Mac</strong> and <strong>Linux</strong>",
"OtherFeatures": "Other Features",
"OtherFeatures1": "Updates <strong>NuGet</strong> and <strong>NPM</strong> packages on your solution easily.",
"OtherFeatures2": "Regenerates already generated pages from scratch.",
"OtherFeatures3": "Creates new solutions",
"ThanksForCreatingProject": "Thanks for Creating Your Project!",
"HotToRunSolution": "How to run your solution?",
"HotToRunSolutionExplanation": "See getting started document to learn how to configure and run your solution.",
"GettingStarted": "Getting Started",
"WebAppDevTutorial": "Web App Dev Tutorial",
"WebAppDevTutorialExplanation": "See web application development tutorial document for a step by step development sample.",
"Document": "Document",
"UsingABPSuiteToCURD": "Using ABP Suite for CRUD Page Generation & Tooling",
"SeeABPSuiteDocument": "See ABP Suite document to learn the usage of ABP Suite.",
"AskQuestionsOnSupport": "You can ask questions on ABP Commercial Support.",
"Documentation": "Documentation",
"SeeModulesDocument": "See modules document for a list of all commercial (pro) modules and their documents.",
"Pricing": "Pricing",
"PricingExplanation": "Choose the features and functionality your business needs today. Easily upgrade as your business grows.",
"Team": "Team",
"Business": "Business",
"Enterprise": "Enterprise",
"Custom": "Custom",
"IncludedDeveloperLicenses": "Included developer licenses",
"CustomLicenceOrAdditionalServices": "Need custom licence or additional services?",
"CustomOrVolumeLicense": "Custom or volume license",
"LiveTrainingSupport": "Live training & support",
"AndMore": "and more",
"AdditionalDeveloperLicense": "Additional developer license",
"ProjectCount": "Project count",
"AllProModules": "All pro modules",
"AllProThemes": "All pro themes",
"AllProStartupTemplates": "All pro startup templates",
"SourceCodeOfAllModules": "Source code of all modules",
"SourceCodeOfAllThemes": "Source code of all themes",
"PerpetualLicense": "Perpetual license",
"UnlimitedServerDeployment": "Unlimited server deployment",
"YearUpgrade": "1 year upgrade",
"YearPremiumForumSupport": "1 year premium forum support",
"ForumSupportIncidentCountYear": "Forum support incident count/year",
"PrivateTicketEmailSupport": "Private ticket & email support",
"BuyNow": "Buy Now"
}
} }

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

@ -16,7 +16,7 @@
"Name": "名称", "Name": "名称",
"EmailAddress": "电子邮件地址", "EmailAddress": "电子邮件地址",
"Developers": "开发者", "Developers": "开发者",
"LicenseType": "许可证类型", "LicenseType": "许可类型",
"Manage": "管理", "Manage": "管理",
"StartDate": "开始日期", "StartDate": "开始日期",
"EndDate": "结束日期", "EndDate": "结束日期",
@ -33,7 +33,258 @@
"EmailNotValid": "请输入有效的电子邮件地址", "EmailNotValid": "请输入有效的电子邮件地址",
"JoinOurMarketingNewsletter": "加入我们的营销简讯", "JoinOurMarketingNewsletter": "加入我们的营销简讯",
"WouldLikeToReceiveMarketingMaterials": "我想收到市场营销资料,例如产品交易和特别优惠.", "WouldLikeToReceiveMarketingMaterials": "我想收到市场营销资料,例如产品交易和特别优惠.",
"StartUsingYourLicenseNow": "立即开始使用你的许可证", "StartUsingYourLicenseNow": "立即开始使用你的许可",
"WelcomePage": "欢迎页面" "WelcomePage": "欢迎页面",
"UnsubscriptionExpireEmail": "退订许可到期提醒邮件",
"UnsubscribeLicenseExpireEmailReminderMessage": "此电子邮件订阅仅提醒你许可的到期日期.",
"UnsubscribeFromLicenseExpireEmails": "如果你不想收到关于许可到期邮件,你可以在任何时间取消订阅.",
"Unsubscribe": "取消订阅",
"NotOrganizationMember": "你不是任何组织的成员.",
"UnsubscribeLicenseExpirationEmailSuccessTitle": "退订成功",
"UnsubscribeLicenseExpirationEmailSuccessMessage": "你不会再收到任何许可到期提醒邮件.",
"IndexPageHeroSection": "<span class=\"first-line\">一个完整的</span><span class=\"second-line\">web开发平台</span><span class=\"third-line\">基于 <img src=\"{0}\" width=\"110\" class=\"ml-1\" /> 框架</span>",
"AbpCommercialShortDescription": "ABP商业版提供了预构建的应用程序模块, 快速的应用程序开发工具, 专业的UI主题, 高级支持等.",
"LiveDemo": "在线演示",
"GetLicence" :"获得许可",
"Application": "应用程序",
"StartupTemplates": "启动模板",
"Startup": "启动",
"Templates": "模板",
"Developer": "开发人员",
"Tools": "工具",
"Premium": "高级",
"PremiumSupport": "高级支持",
"PremiumForumSupport": "高级论坛支持",
"UI": "UI",
"Themes": "主题",
"JoinOurNewsletter": "加入我们的时事通讯",
"Send": "发送",
"Learn": "学习",
"AdditionalServices": "额外的服务",
"WhatIsABPFramework": "什么是ABP框架?",
"OpenSourceBaseFramework": "开源的框架",
"ABPFrameworkExplanation": "<p class=\"lead\">ABP商业版基于ABP框架, 这是一个开源和社区驱动的ASP.NET Core web应用程序开发框架.</p><p>ABP框架提供了出色的基础设施, 使用最佳实践编写可维护,可扩展,可测试的代码.</p><p>基于你已经知道的流行工具. 低学习曲线,容易适应,舒适的开发体检.</p>",
"Modular": "模块化",
"MicroserviceCompatible": "微服务兼容",
"DomainDrivenDesignInfrastructure": "领域驱动设计基础设施",
"MultiTenancy": "多租户",
"DistributedMessaging": "分布式消息",
"DynamicProxying": "动态代理",
"BackgroundJobs": "后台作业",
"AuditLogging": "审计日志",
"BLOBStoring": "BLOB存储",
"BundlingMinification": "捆绑 & 压缩",
"AdvancedLocalization": "高级本地化",
"ManyMore": "以及更多",
"ExploreTheABPFramework": "探索ABP框架",
"WhyUseTheABPCommercial": "为什么使用ABP商业版?",
"WhyUseTheABPCommercialExplanation": "<p class=\"lead mt- 5\">构建企业级的web应用程序可能复杂和耗时.</p><p>ABP商业版提供所有现代企业级基于ASP.NET Core的解决方案所需要的基础设施. 从设计到部署,整个开发生命周期都由ABP的内置功能和模块提供支持.</p>",
"StartupTemplatesShortDescription": "启动模板使你在几秒内快速启动项目.",
"UIFrameworksOptions": "UI框架选项;",
"DatabaseProviderOptions": "数据库提供程序选项;",
"PreBuiltApplicationModules": "预构建的应用程序模块",
"PreBuiltApplicationModulesShortDescription": "大多数常见的应用程序需求已经做为可重用的模块为你开发了.",
"Account": "账户",
"Blogging": "博客",
"Identity": "Identity",
"IdentityServer": "Identity Server",
"Saas": "Saas",
"LanguageManagement": "语言管理",
"TextTemplateManagement": "文本模板管理",
"See All Modules": "查看所有的模块",
"ABPSuite": "ABP Suite",
"AbpSuiteShortDescription": "ABP Suite是ABP商业版的辅助工具.",
"AbpSuiteExplanation": "它让你在几分钟内构建web页面. 它是一个.Net Core全局工具,可以使用命令行安装. 它可以创建一个新的ABP解决方案,从数据库生成前端CURD页面.",
"Details": "详情",
"LeptonTheme": "Lepton主题",
"ProfessionalModernUIThemes": "专业, 现代化的UI主题",
"LeptonThemeExplanation": "Lepton提供了一系列的Bootstrap管理主题,为任何需要管理仪表盘的项目提供了坚实的基础.",
"DefaultTheme": "默认主题",
"MaterialTheme": "Material主题",
"Default2Theme": "默认主题2",
"DarkTheme": "深色主题",
"DarkBlueTheme": "深蓝主题",
"LightTheme": "浅色主题",
"ProudToWorkWith": "荣幸与你合作",
"OurConsumers": "全球50多个国家的数百家企业和开发商使用ABP商业版.",
"JoinOurConsumers": "加它他们并快速构建令人惊叹的产品.",
"AdditionalServicesExplanation": "你是否需要额外或自定义的服务? <strong>我们和我们的合作伙伴可以提供;</strong>",
"CustomProjectDevelopment": "自定义项目开发",
"CustomProjectDevelopmentExplanation": "专为你的自定义的开发人员.",
"PortingExistingProjects": "移植现有项目",
"PortingExistingProjectsExplanation": "将你的迁移传统项目迁移到ABP平台.",
"LiveSupport": "实时支持",
"LiveSupportExplanation": "在你需要时,可以实时提供远程支持选项.",
"Training": "培训",
"TrainingExplanation": "为你的开发人员提供专门培训.",
"OnBoarding": "管理",
"OnBoardingExplanation": "帮助设置你的开发,CI和CD环境.",
"PrioritizedTechnicalSupport": "优先的技术支持",
"PremiumSupportExplanation": "除了社区对ABP框架出色支持外,我们的支持团队会优先回答商业用户的技术问题.",
"SeeTheSupportOptions": "查看支持选项",
"Contact": "联系",
"TellUsWhatYouNeed": "告诉我们你需要什么.",
"YourMessage": "你的消息",
"YourFullName": "你的全名",
"EmailField": "E-mail地址",
"YourEmailAddress": "你的e-mail地址",
"HowMayWeHelpYou": "我们如何帮助你",
"SendMessage": "发送消息",
"Success": "成功",
"WeWillReplyYou.": "你的消息已经发送! 我们会在短时间内给你答复.",
"GoHome": "回到主页面",
"CreateLiveDemo": "创建在线演示",
"RegisterToTheNewsletter": "注册到时事简报以获取有关ABP.IO的消息,比如新发布的内容.",
"EnterYourEmailOrLogin": "输入你的e-mail地址来创建你的演示或者使用你的已有账号<a href=\"{0}\">登录</a>.",
"ApplicationTemplate": "应用程序模板",
"ApplicationTemplateExplanation": "应用程序启动模板用于创建新的web应用程序.",
"EfCoreProvider": "Entity Framework (支持 SQL Server, MySQL, PostgreSQL, Oracle <a href=\"https://docs.microsoft.com/zh-cn/ef/core/providers/\">和其它</a>)",
"AlreadyIncludedInTemplateModules": "以下模块已经包含并配置在此模板中:",
"ApplicationTemplateArchitecture": "应用程序模板还支持UI,API,身份验证服务器物理分离的分层架构.",
"SeeTheGuideOrGoToTheLiveDemo": "查看开发人员指南或者查看在线演示了解更多关于模板的信息.",
"DeveloperGuide": "开发人员指南",
"ModuleTemplate": "模块模块",
"ModuleTemplateExplanation1": "你是否想要创建可在不同应用程序之间重用的模块? 这个启动模板准备好了一切来创建可重用的<strong>应用程序模块</strong>或<strong>微服务</strong>.",
"ModuleTemplateExplanation2": "<p>你可以为一个模块支持单个或多个UI框架, 单个或多个数据库提供程序. 除了单元测试和集成测试基础架构外,启动模板被配置为在一个最小的应用程序中运行和<strong>测试你的模块</strong>. </p> <p>查看开发人员指南了解有关此模板的技术信息.</p>",
"WithAllStyleOptions": "使用所有的样式选项",
"Demo": "演示",
"SeeAllModules": "查看所有的模块",
"ABPCLIExplanation": "ABP CLI(命令行页面)是一个执行基于ABP解决方案的一些常见操作的命令行工具.",
"ABPSuiteEasilyCURD": "ABP Suite是一个使你轻松创建CURD页面的工具",
"WeAreHereToHelp": "我们在这里为你提供<span class=\"zero-text\">帮助</span>",
"BrowseOrAskQuestion": "你可以浏览我们的帮助主题或搜索常见的问题, 或者你可以使用<a href=\"{0}\" class=\"text-success\">联系表单</a>向我们提问.",
"SearchQuestionPlaceholder": "搜索常见的问题",
"WhatIsTheABPCommercial": "什么是ABP商业版?",
"WhatAreDifferencesThanAbpFramework": "ABP框架与ABP商业版有什么不同?",
"ABPCommercialExplanation": "ABP商业版是一套基于开源<a target=\"_blank\" href=\"{0}\">ABP框架</a>之上的高级模块,工具,主题和服务. ABP商业版由ABP框架背后的同一团队开发和支持.",
"WhatAreDifferencesThanABPFrameworkExplanation": "<p> <a target=\"_blank\" href=\"{0}\">ABP框架</a>是模块化,主题化,微服务兼容的ASP.NET Core应用程序开发框架. 它提供了一个完整的架构和强大的基础设施,让你专注于自己的业务代码而不是重复自己的每一个项目. 它基于软件开发的最佳实践和你已经知道的流行工具 </p> <p> ABP框架是完全免费,开源和由社区驱动的. 它还提供了一个免费的主题和一些预构建的模块 (如 identity管理和租户管理).</p>",
"VisitTheFrameworkVSCommercialDocument": "访问以下链接,了解更多信息 <a href=\"{0}\" target=\"_blank\"> {1} </a>",
"ABPCommercialFollowingBenefits": "ABP商业版在ABP框架之上添加了以下好处;",
"Professional": "专业的",
"UIThemes": "UI主题",
"EnterpriseModules": "企业就绪,功能丰富,预构建的<a href=\"{0}\">应用程序模块</a> (如 Identity Server管理, SaaS管理, 语言管理)",
"ToolingToSupport": "支持你的生产力的工具(如. <a href=\"{0}\">ABP Suite</a>)",
"PremiumSupportLink": "高级<a href=\"{0}\" target=\"_blank\">支持</a>",
"WhatDoIDownloadABPCommercial": "购买ABP商业版后我可以下载什么?",
"CreateUnlimitedSolutions": "一旦你购买了ABP商业许可, 你可以创建<a href=\"{0}\">入门</a>文档中描述的无限的解决方案.",
"ABPCommercialSolutionExplanation": "创建新应用程序时,将根据你的首选项获得Visual Studio解决方案(启动模板). 下载的解决方案已为你安装并配置了商业模块和主题. 你可以删除预装的模块,也可以根据需要添加其他模块. 默认情况下所有模块和主题都使用NuGet/NPM软件包.",
"StartDevelopWithTutorials": "下载的解决方案经过精心设计和记录, 你可以根据<a href=\"{0}\">教程</a>来开发自己的业务代码.",
"TryTheCommercialDemo": "你可以尝试<a href=\"{0}\">示例</a>来查看使用ABP商业模板创建的示例应用程序.",
"HowManyProducts": "使用ABP商业版,我可以构建多少不同的产品/解决方案?",
"HowManyProductsExplanation": "创建ABP项目没有限制. 你可以根据需要创建任意数量的项目,进行开发并将其部署到其他服务器.",
"HowManyDevelopers": "有多少开发者可以参与ABP商业版工作?",
"HowManyDevelopersExplanation": "ABP商业许可是针对每个开发人员的. 不同的许可类型具有不同的开发人员限制. 但是你可以在需要时将更多开发人员添加到任何许可类型. 查看许可类型,开发人员限制和额外的开发人员成本的<a href=\"{0}\">价格</a>页面.",
"ChangingLicenseType": "将来更改我的许可类型吗?",
"ChangingLicenseTypeExplanation": "你始终可以在同一许可中添加新的开发人员. 参阅 \"有多少开发者可以参与ABP商业版工作?\". 你还可以通过支付计算出的价格差来升级到更高的许可. 当你升级到更高的许可计划时,可以享受新计划的好处,但是许可升级不会更改许可的到期日期.",
"LicenseExtendUpgradeDiff": "许可扩展和升级有什么区别?",
"LicenseExtendUpgradeDiffExplanation": "<strong>扩展:</strong> 通过扩展/更新许可,你将继续获得高级支持,并获得有关模块和主题的重大更新. 此外你将能够继续创建新项目. 而且你仍然可以使用ABP Suite来加快开发速度.<hr/><strong>升级:</strong> 通过升级许可,你将升级到更高的许可计划,这将使你获得更多好处. 查看 <a href=\"/pricing\">许可比较表</a>来检查许可计划之间的差异. <strong>另一方面,当你升级时你的许可到期日期不会改变!</strong>要延长你的许可终止日期,你需要延长你的许可.",
"LicenseRenewalCost": "一年后的许可续期费用是多少?",
"LicenseRenewalCostExplanation": "你可以通过支付当前许可价格的%80来续订(扩展)你的许可. 例如; 标准ABP团队许可为 <span class=\"text-muted\">$</span>{0} 续订价格是 <span class=\"text-muted\">$</span>{1}.",
"IsSourceCodeIncluded": "我的许可是否包括商业模块和主题的源代码?",
"IsSourceCodeIncludedExplanation1": "取决于你购买的许可类型:",
"IsSourceCodeIncludedExplanation2": "<strong>团队</strong>: 你的解决方案将这些模块和主题作为NuGet和NPM包使用. 它不包括其源代码. 这样,只要有新版本可用,你就可以轻松升级这些模块和主题. 但是,你无法获取模块和主题的源代码.",
"IsSourceCodeIncludedExplanation3": "<strong>商业/企业</strong>: 除了团队许可外,你还可以下载所需的任何模块或主题的源代码. 你甚至可以删除特定模块的NuGet/NPM软件包引用,并将其源代码直接添加到你的解决方案中以完全更改它.",
"IsSourceCodeIncludedExplanation4": "<p>将模块的源代码包含到解决方案中,可以最大程度地自定义该模块. 但是当新版本发布时,将无法自动升级模块.</p><p>这些许可均不包含ABP Suite源代码,该源代码是一个外部工具,可以为你生成代码并帮助你进行开发</p><p>有关许可类型之间的其它差异查看<a href=\"{0}\">定价</a>页面.</p>",
"ChangingDevelopers": "我将来可以更改我组织的注册开发人员吗?",
"ChangingDevelopersExplanation": "除了将新的开发人员添加到你的许可中之外,你还可以更改现有的开发人员(可以删除一个开发人员并将新的开发人员添加到同一位置),而无需任何额外费用.",
"WhatHappensWhenLicenseEnds": "我的许可期限结束后会怎样?",
"WhatHappensWhenLicenseEndsExplanation1": "ABP商业版具有<a href=\"{0}\" target=\"_blank\">永久许可类型</a>.许可到期后, 你可以继续开发你的项目.并且你没有义务续订许可.许可到期后;",
"WhatHappensWhenLicenseEndsExplanation2": "你不能使用ABP商业版创建新的解决方案,但可以永远继续开发现有的应用程序.",
"WhatHappensWhenLicenseEndsExplanation3": "你能够在你的主版本中获得模块和主题的更新.例如;如果你使用的是模块的v3.2.0版本,你仍然可以获得v3.x.x版本的更新.(v3.3.0 v3.5.2…等等)的.但是你无法获得下一个主要版本(如v4.x,v5.x)的更新",
"WhatHappensWhenLicenseEndsExplanation4": "许可到期后,你无法安装添加到ABP商业平台的新模块和主题.",
"WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite.",
"WhatHappensWhenLicenseEndsExplanation6": "你不再获得<a href=\"{0}\">高级支持</a>.",
"WhatHappensWhenLicenseEndsExplanation7": "如果你想继续获得这些好处,可以继续续订. 续订时有20%的折扣.",
"WhenShouldIRenewMyLicense": "我什么时候应该续订我的许可?",
"WhenShouldIRenewMyLicenseExplanation1": "如果你在许可到期后的<strong> 1个月</strong>内续订许可,则许可总价将享受%20的折扣.",
"WhenShouldIRenewMyLicenseExplanation2": "如果你在你的许可过期日期1个月后更新你的许可,更新价格将与许可购买价格相同,你的更新将没有折扣.",
"TrialPlan": "你们有试用计划吗?",
"TrialPlanExplanation": "目前,ABP商业版还没有试用计划.对于团队许可,我们提供30天的退款保证.你可以在30天内要求退款.对于企业营业执照,我们提供30天内60%的退款.这是因为商业和企业许可包含所有模块和主题的完整源代码.",
"DoYouAcceptBankWireTransfer": "你们接受银行电汇吗?",
"DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇.<br />通过银行电汇转账,通过电子邮件将收据和所要求的许可类型发送给我们.<br />我们的国际银行帐户信息:",
"HowToUpgrade": "可用新版本时如何升级现有应用程序?",
"HowToUpgradeExplanation1": "使用ABP商业版创建新应用程序时,所有模块和主题均用作NuGet和NPM软件包. 因此,当有新版本可用时,你可以轻松升级软件包.",
"HowToUpgradeExplanation2": "除了标准的NuGet/NPM升级之外,<a href=\"{0}\"> ABP CLI </a>还提供了一条更新命令,该命令可自动查找和升级解决方案中的所有与ABP相关的软件包.",
"DatabaseSupport": "支持哪些数据库系统?",
"DatabaseSupportExplanation": "ABP框架本身与数据库无关,并且就其性质而言,可以与任何数据库提供程序一起使用. 请参阅<a href=\"{0}\" target=\"_blank\">数据访问文档</a>以获取当前实现的提供程序的列表.",
"UISupport": "支持哪些UI框架?",
"Supported": "支持的",
"UISupportExplanation": "ABP框架本身与UI框架无关,并且可以与任何UI框架一起使用. 但是,并非针对所有UI框架都实现了启动模板,模块UI和主题. 有关UI选项的最新列表,请参见<a href=\"{0}\">入门文档</a>.",
"MicroserviceSupport": "它是否支持微服务架构?",
"MicroserviceSupportExplanation1": "ABP框架的主要目标之一是为创建微服务解决方案提供便利的基础架构. 请参阅<a href=\"{0}\">微服务体系结构</a>文档,以了解它如何帮助创建微服务系统.",
"MicroserviceSupportExplanation2": "所有的ABP商业模块通过<a href=\"{0}\">模块开发最佳实践</a>文档被设计为支持微服务部署场景(使用自己的API和数据库).",
"MicroserviceSupportExplanation3": "我们提供了一个示例<a href=\"{0}\">微服务演示解决方案</a>,该示例演示了一种微服务架构实现,可帮助你创建自己的解决方案.",
"MicroserviceSupportExplanation4": "所以简短的答案是 \"<strong>是的, 它支持微服务体系结构</strong>\".",
"MicroserviceSupportExplanation5": "但是,微服务系统是一个解决方案,每个解决方案都有不同的要求,网络拓扑,通信场景,身份验证可能性,数据库分离/共享决策,运行时配置,第三方系统集成等等.",
"MicroserviceSupportExplanation6": "ABP框架和ABP商业版提供了微服务方案,微服务兼容模块,示例和文档的基础结构,以帮助你构建自己的解决方案. 但是不要期望直接下载为你预先构建的梦想解决方案. 你将需要了解它,并根据需要将某些部分组合在一起.",
"WhereCanIDownloadSourceCode": "在哪里可以下载源代码?",
"WhereCanIDownloadSourceCodeExplanation": "你可以通过ABP Suite或ABP CLI下载所有ABP模块,Angular软件包和主题的源代码. 请参见<a href=\"{0}\">如何下载源代码?</a>",
"ComputerLimitation": "开发人员在开发ABP时可以登录到多少台计算机?",
"ComputerLimitationExplanation": "我们特别允许每个个人/许可的开发人员使用<strong> {0}台计算机</strong>. 每当需要开发人员在第三台计算机上开发ABP商业产品时,都应将电子邮件发送到license@abp.io,以说明情况,然后我们将在系统中进行适当分配.",
"RefundPolicy": "你们有退款政策吗?",
"RefundPolicyExplanation": "你可以在购买许可的<strong>30天</strong>内请求退款.商业和企业许可类型都有源代码下载选项,因此商业和企业(以及任何包含获得源代码的权利的许可)都不能退款.此外,续展和购买第二次许可也不退款.",
"HowCanIRefundVat": "我该如何退还增值税?",
"HowCanIRefundVatExplanation1": "如果你使用2Checkout付款,则可以通过2Checkout帐户退还增值税:",
"HowCanIRefundVatExplanation2": "登录到你的<a href=\"https://secure.2checkout.com/cpanel/login.php\" target=\"_blank\">2Checkout</a>账户",
"HowCanIRefundVatExplanation3": "找到适当的订单,然后按\"退还已延期的增值税\"(输入你的增值税ID",
"HowCanIGetMyInvoice": "我如何获得发票?",
"HowCanIGetMyInvoiceExplanation": "有两个用于购买许可的支付网关:PayU和2Checkout. 如果你通过2Checkout网关购买许可,则它将PDF发票发送到你的电子邮件地址,请参阅<a href=\"https://knowledgecenter.2checkout.com/Documentation/03Billing-and-payments/Payment-operations/How-do-invoices-work>2Checkout发票.</a>如果你是通过PayU网关或通过银行电汇购买的,我们将准备并发送你的发票. 你可以从<a href=\"{0}\">组织管理页面</a>索取发票.",
"Forum": "论坛",
"SupportExplanation": "ABP商业版许可包含由ABP框架专家组成的团队提供的高级论坛支持.",
"PrivateTicket": "私有票",
"PrivateTicketExplanation": "企业许可还包含带有电子邮件和票系统的私人支持.",
"AbpSuiteExplanation1": "ABP Suite使你在几分钟内构建web页面. 它是一个.NET Core Global工具,可以使用命令行安装.",
"AbpSuiteExplanation2": "它可以创建一个新的ABP解决方案, 从数据库生成前端CURD页面. 有关技术概述查看<a href=\"{0}\">文档</a>",
"FastEasy": "快速并且简单",
"AbpSuiteExplanation3": "ABP Suite允许你轻松的创建CURD页面. 你只需要定义你的实体和它的属性, 其它的让ABP Suite帮你完成! ABP Suite会在几秒内为你生成CURD页面和必要的代码. 它支持Angular, MVC和 Blazor用户界面.",
"RichOptions": "丰富的选项",
"AbpSuiteExplanation4": "ABP Suite支持多个UI选项,例如 <a href=\"https://docs.microsoft.com/en-us/aspnet/core/razor-pages\">Razor Pages</a>和<a href=\"https://angular.io\">Angular</a>.它还支持多个数据库, 如 <a href=\"https://www.mongodb.com\">MongoDB</a> 和<strong>EntityFramework Core</strong>支持的所有数据库(MS SQL Server, Oracle, MySql, PostgreSQL 和 <a href=\"https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli\">更多</a>).",
"AbpSuiteExplanation5": "好消息是, 你不必担心这些选项. ABP Suite了解你的项目类型并且将生成的代码放到项目中正确的位置.",
"SourceCode": "源码",
"AbpSuiteExplanation6": "ABP Suite为你生成源代码! 它不会生成魔法文件来生成web页面. ABP Suite为<strong>实体, 仓储, 应用程序, Code First迁移, JavaScript/TypeScript 和 CSHTML/HTML</strong>以及必要的页面生成源代码. ABP Suite根据软件开发的<strong>最佳实践</strong>来生成代码, 所以你不必担心生成的代码的质量.",
"AbpSuiteExplanation7": "由于你有应用程序层中生成的CURD页面的构建块的源代码, 因此你可以轻松修改源码并将自定义/业务逻辑注入到所生成的代码中.",
"CrossPlatform": "跨平台",
"AbpSuiteExplanation8": "ABP Suite使用.NET Core构建并且它是跨平台的. 它在你的本地电脑中运行为web应用程序. 你可以在<strong>Windows</strong>, <strong>Mac</strong>和<strong>Linux</strong>上运行它",
"OtherFeatures": "其他功能",
"OtherFeatures1": "轻松更新你的解决方案的<strong>NuGet</strong>和<strong>NPM</strong>包.",
"OtherFeatures2": "重新生成已经生成的页面.",
"OtherFeatures3": "创建新的解决方案",
"ThanksForCreatingProject": "感谢你创建你的项目!",
"HotToRunSolution": "如何运行你的解决方案?",
"HotToRunSolutionExplanation": "查看入门文档来学习如何配置和运行你的解决方案.",
"GettingStarted": "入门",
"WebAppDevTutorial": "Web应用开发教程",
"WebAppDevTutorialExplanation": "查看web应用程序开发教程文档来一步一步的了解开发示例.",
"Document": "文档",
"UsingABPSuiteToCURD": "使用ABP Suite来生成CURD页面",
"SeeABPSuiteDocument": "查看ABP Suite文档学习如何使用ABP Suite.",
"AskQuestionsOnSupport": "你可以在ABP商业支持提出问题.",
"Documentation": "文档",
"SeeModulesDocument": "查看模块文档获取所有商业(专业)模块列表和它们的文档.",
"Pricing": "价格",
"PricingExplanation": "选择当前业务需要的特性和功能. 随着业务增长轻松升级.",
"Team": "团队",
"Business": "商业",
"Enterprise": "企业",
"Custom": "自定义",
"IncludedDeveloperLicenses": "包括开发人员许可",
"CustomLicenceOrAdditionalServices": "需要自定义许可与额外服务?",
"CustomOrVolumeLicense": "自定义或批量许可",
"LiveTrainingSupport": "现场培训和支持",
"AndMore": "和更多",
"AdditionalDeveloperLicense": "额外的开发人员许可",
"ProjectCount": "项目数量",
"AllProModules": "所有模块",
"AllProThemes": "所有主题",
"AllProStartupTemplates": "所有专业启动模板",
"SourceCodeOfAllModules": "所有模块的源码",
"SourceCodeOfAllThemes": "所有主题的源码",
"PerpetualLicense": "永久的许可",
"UnlimitedServerDeployment": "无限制的服务器部署",
"YearUpgrade": "1年升级",
"YearPremiumForumSupport": "1年高级论坛支持",
"ForumSupportIncidentCountYear": "论坛支持事件数量/年",
"PrivateTicketEmailSupport": "私有票和email支持",
"BuyNow": "现在购买"
} }
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2
docs/zh-Hans/Repositories.md

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

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

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

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarItem.cs

@ -14,10 +14,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
public int Order { get; set; } public int Order { get; set; }
public ToolbarItem([NotNull] Type componentType, int order = 0) [CanBeNull]
public string RequiredPermissionName { get; set; }
public ToolbarItem([NotNull] Type componentType, int order = 0, string requiredPermissionName = null)
{ {
Order = order; Order = order;
ComponentType = Check.NotNull(componentType, nameof(componentType)); ComponentType = Check.NotNull(componentType, nameof(componentType));
RequiredPermissionName = requiredPermissionName;
} }
} }
} }

31
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs

@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Theming; using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
@ -14,7 +17,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
public ToolbarManager( public ToolbarManager(
IOptions<AbpToolbarOptions> options, IOptions<AbpToolbarOptions> options,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IThemeManager themeManager) IThemeManager themeManager)
{ {
@ -35,9 +38,33 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
{ {
await contributor.ConfigureToolbarAsync(context); await contributor.ConfigureToolbarAsync(context);
} }
await CheckPermissionsAsync(scope.ServiceProvider, toolbar);
} }
return toolbar; return toolbar;
} }
protected virtual async Task CheckPermissionsAsync(IServiceProvider serviceProvider, Toolbar toolbar)
{
var requiredPermissionItems = toolbar.Items.Where(x => !x.RequiredPermissionName.IsNullOrWhiteSpace()).ToList();
if (requiredPermissionItems.Any())
{
var permissionChecker = serviceProvider.GetRequiredService<IPermissionChecker>();
var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).Distinct().ToArray());
var toBeDeleted = new HashSet<ToolbarItem>();
foreach (var item in requiredPermissionItems)
{
if (grantResult.Result[item.RequiredPermissionName!] != PermissionGrantResult.Granted)
{
toBeDeleted.Add(item);
}
}
toolbar.Items.RemoveAll(toBeDeleted.Contains);
}
}
} }
} }

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

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

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

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

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs

@ -59,7 +59,6 @@ namespace Volo.Abp.Cli.Commands
await SolutionModuleAdder.AddAsync( await SolutionModuleAdder.AddAsync(
solutionFile, solutionFile,
commandLineArgs.Target, commandLineArgs.Target,
commandLineArgs.Options.GetOrNull(Options.StartupProject.Short, Options.StartupProject.Long),
version, version,
skipDbMigrations, skipDbMigrations,
withSourceCode, withSourceCode,
@ -167,12 +166,6 @@ namespace Volo.Abp.Cli.Commands
public const string Skip = "skip-db-migrations"; public const string Skip = "skip-db-migrations";
} }
public static class StartupProject
{
public const string Short = "sp";
public const string Long = "startup-project";
}
public static class SourceCode public static class SourceCode
{ {
public const string Long = "with-source-code"; public const string Long = "with-source-code";

76
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs

@ -26,7 +26,9 @@ namespace Volo.Abp.Cli.Commands
throw new CliUsageException("DbMigrations folder path is missing!"); throw new CliUsageException("DbMigrations folder path is missing!");
} }
var dbMigratorProjectPath = GetDbMigratorProjectPath(commandLineArgs.Target); var dbMigrationsFolder = commandLineArgs.Target;
var dbMigratorProjectPath = GetDbMigratorProjectPath(dbMigrationsFolder);
if (dbMigratorProjectPath == null) if (dbMigratorProjectPath == null)
{ {
throw new Exception("DbMigrator is not found!"); throw new Exception("DbMigrator is not found!");
@ -37,13 +39,17 @@ namespace Volo.Abp.Cli.Commands
InstallDotnetEfTool(); InstallDotnetEfTool();
} }
var addMigrationCmd = $"cd \"{commandLineArgs.Target}\" && " + var tenantDbContextName = FindTenantDbContextName(dbMigrationsFolder);
$"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\""; var dbContextName = tenantDbContextName != null ?
FindDbContextName(dbMigrationsFolder)
: null;
var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbContextName, "Migrations");
var tenantMigrationOutput = tenantDbContextName != null ?
AddMigrationAndGetOutput(dbMigrationsFolder, tenantDbContextName, "TenantMigrations")
: null;
var output = CmdHelper.RunCmdAndGetOutput(addMigrationCmd); if (CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput))
if (output.Contains("Done.") &&
output.Contains("To undo this action") &&
output.Contains("ef migrations remove"))
{ {
// Migration added successfully // Migration added successfully
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run"); CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run");
@ -51,22 +57,70 @@ namespace Volo.Abp.Cli.Commands
} }
else else
{ {
var exceptionMsg = "Migrations failed! The following command didn't run successfully:" + var exceptionMsg = "Migrations failed! A migration command didn't run successfully:" +
Environment.NewLine + Environment.NewLine +
addMigrationCmd + Environment.NewLine + migrationOutput +
Environment.NewLine + output; Environment.NewLine +
Environment.NewLine + tenantMigrationOutput;
Logger.LogError(exceptionMsg); Logger.LogError(exceptionMsg);
throw new Exception(exceptionMsg); throw new Exception(exceptionMsg);
} }
} }
private string FindTenantDbContextName(string dbMigrationsFolder)
{
var tenantDbContext = Directory
.GetFiles(dbMigrationsFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories)
.FirstOrDefault();
if (tenantDbContext == null)
{
return null;
}
return Path.GetFileName(tenantDbContext).RemovePostFix(".cs");
}
private string FindDbContextName(string dbMigrationsFolder)
{
var dbContext = Directory
.GetFiles(dbMigrationsFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories)
.FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs"));
if (dbContext == null)
{
return null;
}
return Path.GetFileName(dbContext).RemovePostFix(".cs");
}
private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext, string outputDirectory)
{
var dbContextOption = string.IsNullOrWhiteSpace(dbContext)
? string.Empty
: $"--context {dbContext}";
var addMigrationCmd = $"cd \"{dbMigrationsFolder}\" && " +
$"dotnet ef migrations add Initial --output-dir {outputDirectory} {dbContextOption}";
return CmdHelper.RunCmdAndGetOutput(addMigrationCmd);
}
private static bool IsDotNetEfToolInstalled() private static bool IsDotNetEfToolInstalled()
{ {
var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g"); var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g");
return output.Contains("dotnet-ef"); return output.Contains("dotnet-ef");
} }
private static bool CheckMigrationOutput(string output)
{
return output == null || (output.Contains("Done.") &&
output.Contains("To undo this action") &&
output.Contains("ef migrations remove"));
}
private void InstallDotnetEfTool() private void InstallDotnetEfTool()
{ {
Logger.LogInformation("Installing dotnet-ef tool..."); Logger.LogInformation("Installing dotnet-ef tool...");
@ -95,4 +149,4 @@ namespace Volo.Abp.Cli.Commands
return string.Empty; return string.Empty;
} }
} }
} }

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

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

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

@ -54,7 +54,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
private void AdjustOracleDbContextOptionsBuilder(ProjectBuildContext context) private void AdjustOracleDbContextOptionsBuilder(ProjectBuildContext context)
{ {
var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase))
?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
dbContextFactoryFile.ReplaceText("new DbContextOptionsBuilder", dbContextFactoryFile.ReplaceText("new DbContextOptionsBuilder",
$"(DbContextOptionsBuilder<{context.BuildArgs.SolutionName.ProjectName}MigrationsDbContext>) new DbContextOptionsBuilder"); $"(DbContextOptionsBuilder<{context.BuildArgs.SolutionName.ProjectName}MigrationsDbContext>) new DbContextOptionsBuilder");
@ -62,7 +63,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
private void AddMySqlServerVersion(ProjectBuildContext context) private void AddMySqlServerVersion(ProjectBuildContext context)
{ {
var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase))
?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
dbContextFactoryFile.ReplaceText("configuration.GetConnectionString(\"Default\")", dbContextFactoryFile.ReplaceText("configuration.GetConnectionString(\"Default\")",
"configuration.GetConnectionString(\"Default\"), MySqlServerVersion.LatestSupportedServerVersion"); "configuration.GetConnectionString(\"Default\"), MySqlServerVersion.LatestSupportedServerVersion");
@ -90,7 +92,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase)); var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase));
efCoreModuleClass.ReplaceText(oldUseMethod, newUseMethodForEfModule); efCoreModuleClass.ReplaceText(oldUseMethod, newUseMethodForEfModule);
var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase))
?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase));
dbContextFactoryFile.ReplaceText(oldUseMethod, newUseMethodForDbContext); dbContextFactoryFile.ReplaceText(oldUseMethod, newUseMethodForDbContext);
} }
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -24,6 +24,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
{ {
var steps = new List<ProjectBuildPipelineStep>(); var steps = new List<ProjectBuildPipelineStep>();
ConfigureTenantSchema(context, steps);
SwitchDatabaseProvider(context, steps); SwitchDatabaseProvider(context, steps);
DeleteUnrelatedProjects(context, steps); DeleteUnrelatedProjects(context, steps);
RemoveMigrations(context, steps); RemoveMigrations(context, steps);
@ -38,6 +39,19 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
return steps; return steps;
} }
private static void ConfigureTenantSchema(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("separate-tenant-schema"))
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations"));
steps.Add(new AppTemplateProjectRenameStep("MyCompanyName.MyProjectName.EntityFrameworkCore.SeparateDbMigrations", "MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations"));
}
else
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.SeparateDbMigrations"));
}
}
private static void SwitchDatabaseProvider(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps) private static void SwitchDatabaseProvider(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{ {
if (context.BuildArgs.DatabaseProvider == DatabaseProvider.MongoDb) if (context.BuildArgs.DatabaseProvider == DatabaseProvider.MongoDb)
@ -284,6 +298,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
SemanticVersion.Parse(context.BuildArgs.Version) > new SemanticVersion(4,1,99)) SemanticVersion.Parse(context.BuildArgs.Version) > new SemanticVersion(4,1,99))
{ {
steps.Add(new RemoveFolderStep("/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations")); steps.Add(new RemoveFolderStep("/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations"));
steps.Add(new RemoveFolderStep("/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/TenantMigrations"));
} }
} }

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

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

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

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

59
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs

@ -17,14 +17,38 @@ namespace Volo.Abp.Cli.ProjectModification
Logger = NullLogger<EfCoreMigrationManager>.Instance; Logger = NullLogger<EfCoreMigrationManager>.Instance;
} }
public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject) public void AddMigration(string dbMigrationsCsprojFile, string module)
{ {
var dbMigrationsProjectFolder = Path.GetDirectoryName(dbMigrationsCsprojFile);
var moduleName = ParseModuleName(module); var moduleName = ParseModuleName(module);
var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix();
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) + var tenantDbContextName = FindTenantDbContextName(dbMigrationsProjectFolder);
"\" && dotnet ef migrations add " + migrationName + var dbContextName = tenantDbContextName != null ?
GetStartupProjectOption(startupProject)); FindDbContextName(dbMigrationsProjectFolder)
: null;
if (!string.IsNullOrEmpty(tenantDbContextName))
{
RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, tenantDbContextName, "TenantMigrations");
}
RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, dbContextName, "Migrations");
}
protected virtual void RunAddMigrationCommand(
string dbMigrationsProjectFolder,
string migrationName,
string dbContext,
string outputDirectory)
{
var dbContextOption = string.IsNullOrWhiteSpace(dbContext)
? string.Empty
: $"--context {dbContext}";
CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName}" +
$" --output-dir {outputDirectory}" +
$" {dbContextOption}");
} }
protected virtual string ParseModuleName(string fullModuleName) protected virtual string ParseModuleName(string fullModuleName)
@ -43,9 +67,32 @@ namespace Volo.Abp.Cli.ProjectModification
return "_" + new Random().Next(1, 99999); return "_" + new Random().Next(1, 99999);
} }
protected virtual string GetStartupProjectOption(string startupProject) protected virtual string FindDbContextName(string dbMigrationsFolder)
{ {
return startupProject.IsNullOrWhiteSpace() ? "" : $" -s {startupProject}"; var dbContext = Directory
.GetFiles(dbMigrationsFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories)
.FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs"));
if (dbContext == null)
{
return null;
}
return Path.GetFileName(dbContext).RemovePostFix(".cs");
}
protected virtual string FindTenantDbContextName(string dbMigrationsFolder)
{
var tenantDbContext = Directory
.GetFiles(dbMigrationsFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories)
.FirstOrDefault();
if (tenantDbContext == null)
{
return null;
}
return Path.GetFileName(tenantDbContext).RemovePostFix(".cs");
} }
} }
} }

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

@ -78,7 +78,6 @@ namespace Volo.Abp.Cli.ProjectModification
public virtual async Task AddAsync( public virtual async Task AddAsync(
[NotNull] string solutionFile, [NotNull] string solutionFile,
[NotNull] string moduleName, [NotNull] string moduleName,
string startupProject,
string version, string version,
bool skipDbMigrations = false, bool skipDbMigrations = false,
bool withSourceCode = false, bool withSourceCode = false,
@ -128,7 +127,7 @@ namespace Volo.Abp.Cli.ProjectModification
await RunBundleForBlazorAsync(projectFiles, module); await RunBundleForBlazorAsync(projectFiles, module);
ModifyDbContext(projectFiles, module, startupProject, skipDbMigrations); ModifyDbContext(projectFiles, module, skipDbMigrations);
} }
private async Task RunBundleForBlazorAsync(string[] projectFiles, ModuleWithMastersInfo module) private async Task RunBundleForBlazorAsync(string[] projectFiles, ModuleWithMastersInfo module)
@ -450,8 +449,7 @@ namespace Volo.Abp.Cli.ProjectModification
} }
} }
protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, string startupProject, protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, bool skipDbMigrations = false)
bool skipDbMigrations = false)
{ {
if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName)) if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName))
{ {
@ -463,11 +461,6 @@ namespace Volo.Abp.Cli.ProjectModification
return; return;
} }
if (string.IsNullOrWhiteSpace(startupProject))
{
startupProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrator.csproj"));
}
var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")); var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj"));
if (dbMigrationsProject == null) if (dbMigrationsProject == null)
@ -498,7 +491,7 @@ namespace Volo.Abp.Cli.ProjectModification
{ {
if (addedNewBuilder) if (addedNewBuilder)
{ {
EfCoreMigrationManager.AddMigration(dbMigrationsProject, module.Name, startupProject); EfCoreMigrationManager.AddMigration(dbMigrationsProject, module.Name);
} }
RunMigrator(projectFiles); RunMigrator(projectFiles);

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

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

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

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

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

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal; using Volo.Abp.Internal;
using Volo.Abp.Logging;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp namespace Volo.Abp
@ -71,11 +73,30 @@ namespace Volo.Abp
{ {
using (var scope = ServiceProvider.CreateScope()) using (var scope = ServiceProvider.CreateScope())
{ {
WriteInitLogs(scope.ServiceProvider);
scope.ServiceProvider scope.ServiceProvider
.GetRequiredService<IModuleManager>() .GetRequiredService<IModuleManager>()
.InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider)); .InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
} }
} }
protected virtual void WriteInitLogs(IServiceProvider serviceProvider)
{
var logger = serviceProvider.GetService<ILogger<AbpApplicationBase>>();
if (logger == null)
{
return;
}
var initLogger = serviceProvider.GetRequiredService<IInitLogger>();
foreach (var entry in initLogger.Entries)
{
logger.LogWithLevel(entry.Level, entry.Message, entry.Exception);
}
initLogger.Entries.Clear();
}
protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{ {

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

@ -1,6 +1,7 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Logging;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Reflection; using Volo.Abp.Reflection;
@ -35,6 +36,7 @@ namespace Volo.Abp.Internal
services.TryAddSingleton<IModuleLoader>(moduleLoader); services.TryAddSingleton<IModuleLoader>(moduleLoader);
services.TryAddSingleton<IAssemblyFinder>(assemblyFinder); services.TryAddSingleton<IAssemblyFinder>(assemblyFinder);
services.TryAddSingleton<ITypeFinder>(typeFinder); services.TryAddSingleton<ITypeFinder>(typeFinder);
services.TryAddSingleton<IInitLogger>(new DefaultInitLogger());
services.AddAssemblyOf<IAbpApplication>(); services.AddAssemblyOf<IAbpApplication>();

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

@ -0,0 +1,26 @@
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public class AbpInitLogEntry
{
public LogLevel Level { get; }
public string Message { get; }
[CanBeNull]
public Exception Exception { get; }
public AbpInitLogEntry(
LogLevel level,
string message,
Exception exception)
{
Level = level;
Message = message;
Exception = exception;
}
}
}

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

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public class DefaultInitLogger : IInitLogger
{
public List<AbpInitLogEntry> Entries { get; }
public DefaultInitLogger()
{
Entries = new List<AbpInitLogEntry>();
}
public void Log(
LogLevel logLevel,
string message,
Exception exception = null)
{
Entries.Add(new AbpInitLogEntry(logLevel, message, exception));
}
}
}

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

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Logging
{
public interface IInitLogger
{
public List<AbpInitLogEntry> Entries { get; }
void Log(
LogLevel logLevel,
string message,
Exception exception = null);
}
}

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

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

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

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Logging;
using Volo.Abp.Modularity.PlugIns; using Volo.Abp.Modularity.PlugIns;
namespace Volo.Abp.Modularity namespace Volo.Abp.Modularity
@ -43,14 +44,16 @@ namespace Volo.Abp.Modularity
Type startupModuleType, Type startupModuleType,
PlugInSourceList plugInSources) PlugInSourceList plugInSources)
{ {
var initLogger = services.GetInitLogger();
//All modules starting from the startup module //All modules starting from the startup module
foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType)) foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType, initLogger))
{ {
modules.Add(CreateModuleDescriptor(services, moduleType)); modules.Add(CreateModuleDescriptor(services, moduleType));
} }
//Plugin modules //Plugin modules
foreach (var moduleType in plugInSources.GetAllModules()) foreach (var moduleType in plugInSources.GetAllModules(initLogger))
{ {
if (modules.Any(m => m.Type == moduleType)) if (modules.Any(m => m.Type == moduleType))
{ {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

42
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs

@ -11,60 +11,58 @@ namespace Volo.Abp.Domain.Repositories
public static class MongoDbCoreRepositoryExtensions public static class MongoDbCoreRepositoryExtensions
{ {
[Obsolete("Use GetDatabaseAsync method.")] [Obsolete("Use GetDatabaseAsync method.")]
public static IMongoDatabase GetDatabase<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository) public static IMongoDatabase GetDatabase<TEntity>(this IReadOnlyBasicRepository<TEntity> repository)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().Database; return repository.ToMongoDbRepository().Database;
} }
public static Task<IMongoDatabase> GetDatabaseAsync<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository, CancellationToken cancellationToken = default) public static Task<IMongoDatabase> GetDatabaseAsync<TEntity>(this IReadOnlyBasicRepository<TEntity> repository, CancellationToken cancellationToken = default)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().GetDatabaseAsync(cancellationToken); return repository.ToMongoDbRepository().GetDatabaseAsync(cancellationToken);
} }
[Obsolete("Use GetCollectionAsync method.")] [Obsolete("Use GetCollectionAsync method.")]
public static IMongoCollection<TEntity> GetCollection<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository) public static IMongoCollection<TEntity> GetCollection<TEntity>(this IReadOnlyBasicRepository<TEntity> repository)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().Collection; return repository.ToMongoDbRepository().Collection;
} }
public static Task<IMongoCollection<TEntity>> GetCollectionAsync<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository, CancellationToken cancellationToken = default) public static Task<IMongoCollection<TEntity>> GetCollectionAsync<TEntity>(this IReadOnlyBasicRepository<TEntity> repository, CancellationToken cancellationToken = default)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); return repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken);
} }
[Obsolete("Use GetMongoQueryableAsync method.")] [Obsolete("Use GetMongoQueryableAsync method.")]
public static IMongoQueryable<TEntity> GetMongoQueryable<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository) public static IMongoQueryable<TEntity> GetMongoQueryable<TEntity>(this IReadOnlyBasicRepository<TEntity> repository)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().GetMongoQueryable(); return repository.ToMongoDbRepository().GetMongoQueryable();
} }
public static Task<IMongoQueryable<TEntity>> GetMongoQueryableAsync<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository, CancellationToken cancellationToken = default) public static Task<IMongoQueryable<TEntity>> GetMongoQueryableAsync<TEntity>(this IReadOnlyBasicRepository<TEntity> repository, CancellationToken cancellationToken = default)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken); return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken);
} }
public static Task<IAggregateFluent<TEntity>> GetAggregateAsync<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository, CancellationToken cancellationToken = default) public static Task<IAggregateFluent<TEntity>> GetAggregateAsync<TEntity>(this IReadOnlyBasicRepository<TEntity> repository, CancellationToken cancellationToken = default)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken); return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken);
} }
public static IMongoDbRepository<TEntity, TKey> ToMongoDbRepository<TEntity, TKey>(this IBasicRepository<TEntity, TKey> repository) public static IMongoDbRepository<TEntity> ToMongoDbRepository<TEntity>(this IReadOnlyBasicRepository<TEntity> repository)
where TEntity : class, IEntity<TKey> where TEntity : class, IEntity
{ {
var mongoDbRepository = repository as IMongoDbRepository<TEntity, TKey>; if (repository is IMongoDbRepository<TEntity> mongoDbRepository)
if (mongoDbRepository == null)
{ {
throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository<TEntity, TKey>).AssemblyQualifiedName, nameof(repository)); return mongoDbRepository;
} }
throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository<TEntity>).AssemblyQualifiedName, nameof(repository));
return mongoDbRepository;
} }
} }
} }

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

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

7
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs

@ -71,6 +71,9 @@ namespace Volo.Abp.UI.Navigation
[NotNull] [NotNull]
public ApplicationMenuItemList Items { get; } public ApplicationMenuItemList Items { get; }
[CanBeNull]
public string RequiredPermissionName { get; set; }
/// <summary> /// <summary>
/// Can be used to store a custom object related to this menu item. Optional. /// Can be used to store a custom object related to this menu item. Optional.
/// </summary> /// </summary>
@ -95,7 +98,8 @@ namespace Volo.Abp.UI.Navigation
object customData = null, object customData = null,
string target = null, string target = null,
string elementId = null, string elementId = null,
string cssClass = null) string cssClass = null,
string requiredPermissionName = null)
{ {
Check.NotNullOrWhiteSpace(name, nameof(name)); Check.NotNullOrWhiteSpace(name, nameof(name));
Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); Check.NotNullOrWhiteSpace(displayName, nameof(displayName));
@ -109,6 +113,7 @@ namespace Volo.Abp.UI.Navigation
Target = target; Target = target;
ElementId = elementId ?? GetDefaultElementId(); ElementId = elementId ?? GetDefaultElementId();
CssClass = cssClass; CssClass = cssClass;
RequiredPermissionName = requiredPermissionName;
Items = new ApplicationMenuItemList(); Items = new ApplicationMenuItemList();
} }

58
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.UI.Navigation namespace Volo.Abp.UI.Navigation
@ -13,7 +15,7 @@ namespace Volo.Abp.UI.Navigation
protected IHybridServiceScopeFactory ServiceScopeFactory { get; } protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
public MenuManager( public MenuManager(
IOptions<AbpNavigationOptions> options, IOptions<AbpNavigationOptions> options,
IHybridServiceScopeFactory serviceScopeFactory) IHybridServiceScopeFactory serviceScopeFactory)
{ {
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
@ -32,6 +34,8 @@ namespace Volo.Abp.UI.Navigation
{ {
await contributor.ConfigureMenuAsync(context); await contributor.ConfigureMenuAsync(context);
} }
await CheckPermissionsAsync(scope.ServiceProvider, menu);
} }
NormalizeMenu(menu); NormalizeMenu(menu);
@ -39,14 +43,60 @@ namespace Volo.Abp.UI.Navigation
return menu; return menu;
} }
protected virtual async Task CheckPermissionsAsync(IServiceProvider serviceProvider, IHasMenuItems menuWithItems)
{
var requiredPermissionItems = new List<ApplicationMenuItem>();
GetRequiredPermissionNameMenus(menuWithItems, requiredPermissionItems);
if (requiredPermissionItems.Any())
{
var permissionChecker = serviceProvider.GetRequiredService<IPermissionChecker>();
var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).Distinct().ToArray());
var toBeDeleted = new HashSet<ApplicationMenuItem>();
foreach (var menu in requiredPermissionItems)
{
if (grantResult.Result[menu.RequiredPermissionName!] != PermissionGrantResult.Granted)
{
toBeDeleted.Add(menu);
}
}
RemoveMenus(menuWithItems, toBeDeleted);
}
}
protected virtual void GetRequiredPermissionNameMenus(IHasMenuItems menuWithItems, List<ApplicationMenuItem> output)
{
foreach (var item in menuWithItems.Items)
{
if (!item.RequiredPermissionName.IsNullOrWhiteSpace())
{
output.Add(item);
}
GetRequiredPermissionNameMenus(item, output);
}
}
protected virtual void RemoveMenus(IHasMenuItems menuWithItems, HashSet<ApplicationMenuItem> toBeDeleted)
{
menuWithItems.Items.RemoveAll(toBeDeleted.Contains);
foreach (var item in menuWithItems.Items)
{
RemoveMenus(item, toBeDeleted);
}
}
protected virtual void NormalizeMenu(IHasMenuItems menuWithItems) protected virtual void NormalizeMenu(IHasMenuItems menuWithItems)
{ {
foreach (var menuItem in menuWithItems.Items) foreach (var item in menuWithItems.Items)
{ {
NormalizeMenu(menuItem); NormalizeMenu(item);
} }
menuWithItems.Items.Normalize(); menuWithItems.Items.Normalize();
} }
} }
} }

28
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Permissions/FakePermissionStore.cs

@ -0,0 +1,28 @@
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Permissions
{
public class FakePermissionStore : IPermissionStore, ITransientDependency
{
public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey)
{
var result = (name.Contains("MyComponent1") || name.Contains("MyComponent3"));
return Task.FromResult(result);
}
public Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names, string providerName, string providerKey)
{
var result = new MultiplePermissionGrantResult();
foreach (var name in names)
{
result.Result.Add(name, (name.Contains("MyComponent1") || name.Contains("MyComponent3"))
? PermissionGrantResult.Granted
: PermissionGrantResult.Prohibited);
}
return Task.FromResult(result);
}
}
}

16
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Permissions/TestPermissionDefinitionProvider.cs

@ -0,0 +1,16 @@
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Permissions
{
public class TestPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var group = context.AddGroup("TestGroup");
group.AddPermission("MyComponent1");
group.AddPermission("MyComponent2");
group.AddPermission("MyComponent3");
}
}
}

129
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Toolbars/Toolbar_Tests.cs

@ -0,0 +1,129 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using NSubstitute;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Security.Claims;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
{
public class Toolbar_Tests : AbpAspNetCoreMvcUiThemeSharedTestBase
{
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services.Configure<AbpToolbarOptions>(options =>
{
options.Contributors.Add(new MyToolbarContributor());
options.Contributors.Add(new MyToolbarContributor2());
});
var claims = new List<Claim>() {
new Claim(AbpClaimTypes.UserId, "1fcf46b2-28c3-48d0-8bac-fa53268a2775"),
};
var identity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(identity);
var principalAccessor = Substitute.For<ICurrentPrincipalAccessor>();
principalAccessor.Principal.Returns(ci => claimsPrincipal);
Thread.CurrentPrincipal = claimsPrincipal;
var themeManager = Substitute.For<IThemeManager>();
themeManager.CurrentTheme.Returns(x => null);
services.Replace(ServiceDescriptor.Singleton<IThemeManager>(themeManager));
}
[Fact]
public void AbpToolbarOptions_Should_Contain_Contributors()
{
var options = GetRequiredService<IOptions<AbpToolbarOptions>>().Value;
options.Contributors.Count.ShouldBe(2);
options.Contributors.ShouldContain(x => x.GetType() == typeof(MyToolbarContributor));
options.Contributors.ShouldContain(x => x.GetType() == typeof(MyToolbarContributor2));
}
[Fact]
public async Task ToolbarManager_Should_Return_Toolbar()
{
var toolbarManager = GetRequiredService<ToolbarManager>();
var toolbar = await toolbarManager.GetAsync(StandardToolbars.Main);
toolbar.Items.Count.ShouldBe(3);
toolbar.Items[0].ComponentType.ShouldBe(typeof(MyComponent1));
toolbar.Items[1].ComponentType.ShouldBe(typeof(MyComponent3));
toolbar.Items[2].ComponentType.ShouldBe(typeof(MyComponent4));
}
public class MyToolbarContributor : IToolbarContributor
{
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
{
if (context.Toolbar.Name != StandardToolbars.Main)
{
return Task.CompletedTask;
}
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent1), requiredPermissionName: "MyComponent1"));
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent2), requiredPermissionName: "MyComponent2"));
return Task.CompletedTask;
}
}
public class MyToolbarContributor2 : IToolbarContributor
{
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
{
if (context.Toolbar.Name != StandardToolbars.Main)
{
return Task.CompletedTask;
}
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent3), requiredPermissionName: "MyComponent3"));
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent4)));
return Task.CompletedTask;
}
}
public class MyComponent1 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyComponent1");
}
}
public class MyComponent2 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyComponent2");
}
}
public class MyComponent3 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyComponent3");
}
}
public class MyComponent4 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyComponent4");
}
}
}
}

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

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

2
framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj

@ -9,6 +9,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.UI.Navigation\Volo.Abp.UI.Navigation.csproj" /> <ProjectReference Include="..\..\src\Volo.Abp.UI.Navigation\Volo.Abp.UI.Navigation.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> <ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
</ItemGroup> </ItemGroup>

21
framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs

@ -0,0 +1,21 @@
using Volo.Abp.Authorization;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.UI.Navigation
{
[DependsOn(typeof(AbpUiNavigationModule))]
[DependsOn(typeof(AbpAuthorizationModule))]
[DependsOn(typeof(AbpAutofacModule))]
public class AbpUiNavigationTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor1());
options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor2());
});
}
}
}

28
framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/FakePermissionStore.cs

@ -0,0 +1,28 @@
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.UI.Navigation
{
public class FakePermissionStore : IPermissionStore, ITransientDependency
{
public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey)
{
var result = (name.Contains("Administration") || name.Contains("Dashboard")) && !name.Contains("SubMenu1");
return Task.FromResult(result);
}
public Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names, string providerName, string providerKey)
{
var result = new MultiplePermissionGrantResult();
foreach (var name in names)
{
result.Result.Add(name, (name.Contains("Administration") || name.Contains("Dashboard")) && !name.Contains("SubMenu1")
? PermissionGrantResult.Granted
: PermissionGrantResult.Prohibited);
}
return Task.FromResult(result);
}
}
}

60
framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs

@ -1,13 +1,17 @@
using System.Threading.Tasks; using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Shouldly; using Shouldly;
using Volo.Abp.Modularity; using Volo.Abp.Security.Claims;
using Volo.Abp.Testing; using Volo.Abp.Testing;
using Xunit; using Xunit;
namespace Volo.Abp.UI.Navigation namespace Volo.Abp.UI.Navigation
{ {
public class MenuManager_Tests : AbpIntegratedTest<MenuManager_Tests.TestModule> public class MenuManager_Tests : AbpIntegratedTest<AbpUiNavigationTestModule>
{ {
private readonly IMenuManager _menuManager; private readonly IMenuManager _menuManager;
@ -16,6 +20,24 @@ namespace Volo.Abp.UI.Navigation
_menuManager = ServiceProvider.GetRequiredService<IMenuManager>(); _menuManager = ServiceProvider.GetRequiredService<IMenuManager>();
} }
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
protected override void AfterAddApplication(IServiceCollection services)
{
var claims = new List<Claim>() {
new Claim(AbpClaimTypes.UserId, "1fcf46b2-28c3-48d0-8bac-fa53268a2775"),
};
var identity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(identity);
var principalAccessor = Substitute.For<ICurrentPrincipalAccessor>();
principalAccessor.Principal.Returns(ci => claimsPrincipal);
Thread.CurrentPrincipal = claimsPrincipal;
}
[Fact] [Fact]
public async Task Should_Get_Menu() public async Task Should_Get_Menu()
{ {
@ -29,22 +51,8 @@ namespace Volo.Abp.UI.Navigation
mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement"); mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement");
mainMenu.Items[1].Items[1].Name.ShouldBe("Administration.RoleManagement"); mainMenu.Items[1].Items[1].Name.ShouldBe("Administration.RoleManagement");
mainMenu.Items[1].Items[2].Name.ShouldBe("Administration.DashboardSettings"); mainMenu.Items[1].Items[2].Name.ShouldBe("Administration.DashboardSettings");
mainMenu.Items[1].Items[3].Name.ShouldBe("Administration.SubMenu1"); mainMenu.Items[1].Items[3].Name.ShouldBe("Administration.SubMenu1"); //No need permission.
mainMenu.Items[1].Items[3].Items[0].Name.ShouldBe("Administration.SubMenu1.1"); // Administration.SubMenu1.1 and Administration.SubMenu1.2 are removed because of don't have permissions.
mainMenu.Items[1].Items[3].Items[1].Name.ShouldBe("Administration.SubMenu1.2");
}
[DependsOn(typeof(AbpUiNavigationModule))]
public class TestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new TestMenuContributor1());
options.MenuContributors.Add(new TestMenuContributor2());
});
}
} }
/* Adds menu items: /* Adds menu items:
@ -65,8 +73,8 @@ namespace Volo.Abp.UI.Navigation
var administration = context.Menu.GetAdministration(); var administration = context.Menu.GetAdministration();
administration.AddItem(new ApplicationMenuItem("Administration.UserManagement", "User Management", url: "/admin/users")); administration.AddItem(new ApplicationMenuItem("Administration.UserManagement", "User Management", url: "/admin/users", requiredPermissionName: "Administration.UserManagement"));
administration.AddItem(new ApplicationMenuItem("Administration.RoleManagement", "Role Management", url: "/admin/roles")); administration.AddItem(new ApplicationMenuItem("Administration.RoleManagement", "Role Management", url: "/admin/roles", requiredPermissionName: "Administration.RoleManagement"));
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -86,16 +94,16 @@ namespace Volo.Abp.UI.Navigation
return Task.CompletedTask; return Task.CompletedTask;
} }
context.Menu.Items.Insert(0, new ApplicationMenuItem("Dashboard", "Dashboard", url: "/dashboard")); context.Menu.Items.Insert(0, new ApplicationMenuItem("Dashboard", "Dashboard", url: "/dashboard", requiredPermissionName: "Dashboard"));
var administration = context.Menu.GetAdministration(); var administration = context.Menu.GetAdministration();
administration.AddItem(new ApplicationMenuItem("Administration.DashboardSettings", "Dashboard Settings", url: "/admin/settings/dashboard")); administration.AddItem(new ApplicationMenuItem("Administration.DashboardSettings", "Dashboard Settings", url: "/admin/settings/dashboard", requiredPermissionName: "Administration.DashboardSettings"));
administration.AddItem( administration.AddItem(
new ApplicationMenuItem("Administration.SubMenu1", "Sub menu 1") new ApplicationMenuItem("Administration.SubMenu1", "Sub menu 1", url: "/submenu1")
.AddItem(new ApplicationMenuItem("Administration.SubMenu1.1", "Sub menu 1.1", url: "/submenu1/submenu1_1")) .AddItem(new ApplicationMenuItem("Administration.SubMenu1.1", "Sub menu 1.1", url: "/submenu1/submenu1_1", requiredPermissionName: "Administration.SubMenu1.1"))
.AddItem(new ApplicationMenuItem("Administration.SubMenu1.2", "Sub menu 1.2", url: "/submenu1/submenu1_2")) .AddItem(new ApplicationMenuItem("Administration.SubMenu1.2", "Sub menu 1.2", url: "/submenu1/submenu1_2", requiredPermissionName: "Administration.SubMenu1.2"))
); );
return Task.CompletedTask; return Task.CompletedTask;

24
framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/TestPermissionDefinitionProvider.cs

@ -0,0 +1,24 @@
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.UI.Navigation
{
public class TestPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var group = context.AddGroup("TestGroup");
group.AddPermission("Dashboard");
group.AddPermission("Administration");
group.AddPermission("Administration.UserManagement");
group.AddPermission("Administration.RoleManagement");
group.AddPermission("Administration.DashboardSettings");
group.AddPermission("Administration.SubMenu1");
group.AddPermission("Administration.SubMenu1.1");
group.AddPermission("Administration.SubMenu1.2");
}
}
}

17
modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs

@ -14,22 +14,17 @@ namespace Volo.Blogging.Admin
} }
} }
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) private Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{ {
var l = context.GetLocalizer<BloggingResource>(); var l = context.GetLocalizer<BloggingResource>();
if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management)) var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"], requiredPermissionName: BloggingPermissions.Blogs.Management);
{
var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]);
//TODO: Using the same permission. Reconsider. managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs", requiredPermissionName: BloggingPermissions.Blogs.Management));
if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management))
{
managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs"));
}
context.Menu.GetAdministration().AddItem(managementRootMenuItem); context.Menu.GetAdministration().AddItem(managementRootMenuItem);
}
return Task.CompletedTask;
} }
} }
} }

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

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

2007
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210226112040_BlogPost_Author.Designer.cs

File diff suppressed because it is too large

109
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210226112040_BlogPost_Author.cs

@ -0,0 +1,109 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class BlogPost_Author : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_CreatorId",
table: "CmsBlogPosts");
migrationBuilder.DropForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_DeleterId",
table: "CmsBlogPosts");
migrationBuilder.DropForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_LastModifierId",
table: "CmsBlogPosts");
migrationBuilder.DropIndex(
name: "IX_CmsBlogPosts_CreatorId",
table: "CmsBlogPosts");
migrationBuilder.DropIndex(
name: "IX_CmsBlogPosts_DeleterId",
table: "CmsBlogPosts");
migrationBuilder.DropIndex(
name: "IX_CmsBlogPosts_LastModifierId",
table: "CmsBlogPosts");
migrationBuilder.AddColumn<Guid>(
name: "AuthorId",
table: "CmsBlogPosts",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
migrationBuilder.CreateIndex(
name: "IX_CmsBlogPosts_AuthorId",
table: "CmsBlogPosts",
column: "AuthorId");
migrationBuilder.AddForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_AuthorId",
table: "CmsBlogPosts",
column: "AuthorId",
principalTable: "CmsUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_AuthorId",
table: "CmsBlogPosts");
migrationBuilder.DropIndex(
name: "IX_CmsBlogPosts_AuthorId",
table: "CmsBlogPosts");
migrationBuilder.DropColumn(
name: "AuthorId",
table: "CmsBlogPosts");
migrationBuilder.CreateIndex(
name: "IX_CmsBlogPosts_CreatorId",
table: "CmsBlogPosts",
column: "CreatorId");
migrationBuilder.CreateIndex(
name: "IX_CmsBlogPosts_DeleterId",
table: "CmsBlogPosts",
column: "DeleterId");
migrationBuilder.CreateIndex(
name: "IX_CmsBlogPosts_LastModifierId",
table: "CmsBlogPosts",
column: "LastModifierId");
migrationBuilder.AddForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_CreatorId",
table: "CmsBlogPosts",
column: "CreatorId",
principalTable: "CmsUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_DeleterId",
table: "CmsBlogPosts",
column: "DeleterId",
principalTable: "CmsUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_CmsBlogPosts_CmsUsers_LastModifierId",
table: "CmsBlogPosts",
column: "LastModifierId",
principalTable: "CmsUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

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

File diff suppressed because it is too large

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

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

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

File diff suppressed because it is too large

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

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

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

File diff suppressed because it is too large

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

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

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

@ -1257,6 +1257,9 @@ namespace Volo.CmsKit.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AuthorId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("BlogId") b.Property<Guid>("BlogId")
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
@ -1266,6 +1269,10 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(40)") .HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp"); .HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationTime") b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2") .HasColumnType("datetime2")
.HasColumnName("CreationTime"); .HasColumnName("CreationTime");
@ -1320,11 +1327,7 @@ namespace Volo.CmsKit.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("CreatorId"); b.HasIndex("AuthorId");
b.HasIndex("DeleterId");
b.HasIndex("LastModifierId");
b.HasIndex("Slug", "BlogId"); b.HasIndex("Slug", "BlogId");
@ -1476,6 +1479,11 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("datetime2") .HasColumnType("datetime2")
.HasColumnName("DeletionTime"); .HasColumnName("DeletionTime");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties") b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)") .HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties"); .HasColumnName("ExtraProperties");
@ -1529,6 +1537,10 @@ namespace Volo.CmsKit.Migrations
.HasColumnType("nvarchar(40)") .HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp"); .HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationTime") b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2") .HasColumnType("datetime2")
.HasColumnName("CreationTime"); .HasColumnName("CreationTime");
@ -1952,23 +1964,13 @@ namespace Volo.CmsKit.Migrations
modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b =>
{ {
b.HasOne("Volo.CmsKit.Users.CmsUser", "Creator") b.HasOne("Volo.CmsKit.Users.CmsUser", "Author")
.WithMany() .WithMany()
.HasForeignKey("CreatorId"); .HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
b.HasOne("Volo.CmsKit.Users.CmsUser", "Deleter") .IsRequired();
.WithMany()
.HasForeignKey("DeleterId");
b.HasOne("Volo.CmsKit.Users.CmsUser", "LastModifier")
.WithMany()
.HasForeignKey("LastModifierId");
b.Navigation("Creator");
b.Navigation("Deleter");
b.Navigation("LastModifier"); b.Navigation("Author");
}); });
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b =>

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

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

3
modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json

@ -7,6 +7,7 @@
"@abp/cms-kit": "4.2.1", "@abp/cms-kit": "4.2.1",
"@abp/tui-editor": "^4.2.1", "@abp/tui-editor": "^4.2.1",
"tui-code-snippet": "1.5.2", "tui-code-snippet": "1.5.2",
"@abp/uppy": "^4.2.1" "@abp/uppy": "^4.2.1",
"slugify": "1.4.6"
} }
} }

21
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/LICENSE

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Simeon Velichkov <simeonvelichkov@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

90
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/README.md

@ -0,0 +1,90 @@
# slugify
[![npm-version]][npm] [![travis-ci]][travis] [![coveralls-status]][coveralls]
```js
var slugify = require('slugify')
slugify('some string') // some-string
// if you prefer something other than '-' as separator
slugify('some string', '_') // some_string
```
- Vanilla ES5 JavaScript
- No dependencies
- Coerces foreign symbols to their English equivalent (check out the [charMap][charmap] for more details)
- Works in the browser (window.slugify) and AMD/CommonJS-flavored module loaders
## Options
```js
slugify('some string', {
replacement: '-', // replace spaces with replacement character, defaults to `-`
remove: undefined, // remove characters that match regex, defaults to `undefined`
lower: false, // convert to lower case, defaults to `false`
strict: false, // strip special characters except replacement, defaults to `false`
locale: 'vi' // language code of the locale to use
})
```
## Remove
For example, to remove `*+~.()'"!:@` from the result slug, you can use `slugify('..', {remove: /[*+~.()'"!:@]/g})`.
## Locales
The main `charmap.json` file contains all known characters and their transliteration. All new characters should be added there first. In case you stumble upon a character already set in `charmap.json`, but not transliterated correctly according to your language, then you have to add those characters in `locales.json` to override the already existing transliteration in `charmap.json`, but for your locale only.
You can get the correct language code of your language from [here](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
## Extend
Out of the box `slugify` comes with support for a handful of Unicode symbols. For example the `☢` (radioactive) symbol is not defined in the [`charMap`][charmap] and therefore it will be stripped by default:
```js
slugify('unicode ♥ is ☢') // unicode-love-is
```
However you can extend the supported symbols, or override the existing ones with your own:
```js
slugify.extend({'☢': 'radioactive'})
slugify('unicode ♥ is ☢') // unicode-love-is-radioactive
```
Keep in mind that the `extend` method extends/overrides the default `charMap` for the entire process. In case you need a fresh instance of the slugify's `charMap` object you have to clean up the module cache first:
```js
delete require.cache[require.resolve('slugify')]
var slugify = require('slugify')
```
## Contribute
1. Add chars to `charmap.json`
2. Run tests `npm test`
3. The tests will build the charmap in `index.js` and will sort the `charmap.json`
4. Commit **all** modified files
---
> This module was originally a vanilla javascript port of [node-slug][node-slug].<br>
> Note that the original [slug][slug] module has been ported to vanilla javascript too.<br>
> One major difference between the two modules is that `slugify` does not depend on the external [unicode][unicode] module.
[npm-version]: https://img.shields.io/npm/v/slugify.svg?style=flat-square (NPM Package Version)
[travis-ci]: https://img.shields.io/travis/simov/slugify/master.svg?style=flat-square (Build Status - Travis CI)
[coveralls-status]: https://img.shields.io/coveralls/simov/slugify.svg?style=flat-square (Test Coverage - Coveralls)
[npm]: https://www.npmjs.com/package/slugify
[travis]: https://travis-ci.org/simov/slugify
[coveralls]: https://coveralls.io/r/simov/slugify?branch=master
[node-slug]: https://github.com/dodo/node-slug
[slug]: https://www.npmjs.com/package/slug
[unicode]: https://www.npmjs.com/package/unicode
[index]: https://github.com/simov/slugify/blob/master/index.js
[charmap]: https://github.com/simov/slugify/blob/master/config/charmap.json

40
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/package.json

@ -0,0 +1,40 @@
{
"name": "slugify",
"version": "1.4.6",
"description": "Slugifies a String",
"keywords": [
"slugify",
"slug",
"url",
"urlify"
],
"license": "MIT",
"homepage": "https://github.com/simov/slugify",
"author": "Simeon Velichkov <simeonvelichkov@gmail.com> (https://simov.github.io)",
"repository": {
"type": "git",
"url": "https://github.com/simov/slugify.git"
},
"devDependencies": {
"coveralls": "^3.1.0",
"mocha": "^7.2.0",
"nyc": "^15.1.0"
},
"main": "./slugify.js",
"files": [
"LICENSE",
"README.md",
"slugify.d.ts",
"slugify.js"
],
"types": "slugify.d.ts",
"scripts": {
"build": "node bin/build",
"test:ci": "npx mocha --recursive",
"test:cov": "npx nyc --reporter=lcov --reporter=text-summary mocha -- --recursive",
"test": "npm run build && npm run test:ci"
},
"engines": {
"node": ">=8.0.0"
}
}

23
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/slugify.d.ts

@ -0,0 +1,23 @@
declare module slugify {
type ExtendArgs = {
[key: string]: any;
}
export function extend (args: ExtendArgs): void;
}
declare function slugify(
string: string,
options?:
| {
replacement?: string;
remove?: RegExp;
lower?: boolean;
strict?: boolean;
locale?: string;
}
| string,
): string;
export default slugify;

66
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/slugify/slugify.js

File diff suppressed because one or more lines are too long

5
modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock

@ -3032,6 +3032,11 @@ signal-exit@^3.0.0:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
slugify@1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.6.tgz#ef288d920a47fb01c2be56b3487b6722f5e34ace"
integrity sha512-ZdJIgv9gdrYwhXqxsH9pv7nXxjUEyQ6nqhngRxoAAOlmMGA28FDq5O4/5US4G2/Nod7d1ovNcgURQJ7kHq50KQ==
snapdragon-node@^2.0.1: snapdragon-node@^2.0.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save