Browse Source

Merge branch 'dev' into text-template

pull/16754/head
maliming 3 years ago
parent
commit
82d9c6faf0
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 16
      .github/workflows/auto-pr.yml
  2. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 58
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
  4. 2
      common.props
  5. 2
      delete-bin-obj.ps1
  6. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/01-microservice-solution.png
  7. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/02-run-single-service.png
  8. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/03-tye-yaml-file.png
  9. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/04-tye-dashboard.png
  10. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/05-example-microservice-solution.png
  11. BIN
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/06-abp-studio-kubernetes-tunnel-how-it-works.png
  12. 226
      docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/POST.md
  13. 28
      docs/en/Migration-Guides/Abp-7_3.md
  14. 1
      docs/en/Migration-Guides/Index.md
  15. 2
      docs/en/UI/Angular/Authorization.md
  16. 23
      docs/en/UI/AspNetCore/Security-Headers.md
  17. 12
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
  18. 9
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/IgnoreAbpSecurityHeader.cs
  19. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs
  20. 2
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/JsonAuditSerializer_Test.cs
  21. 68
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml
  22. 6
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml.cs
  23. 7
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs
  24. 8
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs
  25. 18
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureSaver.cs
  26. 47
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/CalculateHash_Tests.cs
  27. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs
  28. 54
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs
  29. 22
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecord.cs
  30. 18
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs
  31. 43
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs
  32. 2
      npm/lerna.json
  33. 30
      npm/ng-packs/package.json
  34. 6
      npm/ng-packs/packages/account-core/package.json
  35. 6
      npm/ng-packs/packages/account/package.json
  36. 19
      npm/ng-packs/packages/account/src/lib/account-routing.module.ts
  37. 9
      npm/ng-packs/packages/account/src/lib/guards/authentication-flow.guard.ts
  38. 28
      npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts
  39. 8
      npm/ng-packs/packages/components/package.json
  40. 4
      npm/ng-packs/packages/core/package.json
  41. 9
      npm/ng-packs/packages/core/src/lib/abstracts/abstract-guard.ts
  42. 7
      npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts
  43. 1
      npm/ng-packs/packages/core/src/lib/abstracts/index.ts
  44. 28
      npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts
  45. 4
      npm/ng-packs/packages/feature-management/package.json
  46. 12
      npm/ng-packs/packages/generators/package.json
  47. 8
      npm/ng-packs/packages/identity/package.json
  48. 57
      npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts
  49. 16
      npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts
  50. 8
      npm/ng-packs/packages/oauth/package.json
  51. 30
      npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts
  52. 4
      npm/ng-packs/packages/permission-management/package.json
  53. 2
      npm/ng-packs/packages/schematics/package.json
  54. 6
      npm/ng-packs/packages/setting-management/package.json
  55. 6
      npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts
  56. 6
      npm/ng-packs/packages/tenant-management/package.json
  57. 59
      npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts
  58. 8
      npm/ng-packs/packages/tenant-management/src/lib/tenant-management-routing.module.ts
  59. 6
      npm/ng-packs/packages/theme-basic/package.json
  60. 4
      npm/ng-packs/packages/theme-shared/package.json
  61. 4
      npm/packs/anchor-js/package.json
  62. 4
      npm/packs/aspnetcore.components.server.basictheme/package.json
  63. 6
      npm/packs/aspnetcore.components.server.theming/package.json
  64. 4
      npm/packs/aspnetcore.mvc.ui.theme.basic/package.json
  65. 34
      npm/packs/aspnetcore.mvc.ui.theme.shared/package.json
  66. 2
      npm/packs/aspnetcore.mvc.ui/package-lock.json
  67. 2
      npm/packs/aspnetcore.mvc.ui/package.json
  68. 10
      npm/packs/blogging/package.json
  69. 2
      npm/packs/bootstrap-datepicker/package.json
  70. 2
      npm/packs/bootstrap-daterangepicker/package.json
  71. 4
      npm/packs/bootstrap/package.json
  72. 2
      npm/packs/chart.js/package.json
  73. 4
      npm/packs/clipboard/package.json
  74. 12
      npm/packs/cms-kit.admin/package.json
  75. 6
      npm/packs/cms-kit.public/package.json
  76. 6
      npm/packs/cms-kit/package.json
  77. 4
      npm/packs/codemirror/package.json
  78. 4
      npm/packs/core/package.json
  79. 4
      npm/packs/cropperjs/package.json
  80. 4
      npm/packs/datatables.net-bs4/package.json
  81. 4
      npm/packs/datatables.net-bs5/package.json
  82. 4
      npm/packs/datatables.net/package.json
  83. 12
      npm/packs/docs/package.json
  84. 2
      npm/packs/flag-icon-css/package.json
  85. 2
      npm/packs/flag-icons/package.json
  86. 4
      npm/packs/font-awesome/package.json
  87. 4
      npm/packs/highlight.js/package.json
  88. 4
      npm/packs/jquery-form/package.json
  89. 4
      npm/packs/jquery-validation-unobtrusive/package.json
  90. 4
      npm/packs/jquery-validation/package.json
  91. 4
      npm/packs/jquery/package.json
  92. 4
      npm/packs/jstree/package.json
  93. 4
      npm/packs/lodash/package.json
  94. 4
      npm/packs/luxon/package.json
  95. 4
      npm/packs/malihu-custom-scrollbar-plugin/package.json
  96. 4
      npm/packs/markdown-it/package.json
  97. 2
      npm/packs/moment/package.json
  98. 4
      npm/packs/owl.carousel/package.json
  99. 4
      npm/packs/popper.js/package.json
  100. 6
      npm/packs/prismjs/package.json

16
.github/workflows/auto-pr.yml

@ -1,8 +1,8 @@
name: Merge branch dev with rel-7.2
name: Merge branch dev with rel-7.3
on:
push:
branches:
- rel-7.2
- rel-7.3
permissions:
contents: read
@ -18,13 +18,13 @@ jobs:
ref: dev
- name: Reset promotion branch
run: |
git fetch origin rel-7.2:rel-7.2
git reset --hard rel-7.2
git fetch origin rel-7.3:rel-7.3
git reset --hard rel-7.3
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
branch: auto-merge/rel-7-2/${{github.run_number}}
title: Merge branch dev with rel-7.2
body: This PR generated automatically to merge dev with rel-7.2. Please review the changed files before merging to prevent any errors that may occur.
branch: auto-merge/rel-7-3/${{github.run_number}}
title: Merge branch dev with rel-7.3
body: This PR generated automatically to merge dev with rel-7.3. Please review the changed files before merging to prevent any errors that may occur.
reviewers: ${{github.actor}}
token: ${{ github.token }}
token: ${{ github.token }}

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

@ -461,6 +461,9 @@
"UnitPrice": "Unit Price",
"OverallDiscountAmount": "Overall Discount Amount",
"DiscountAmount": "Discount Amount",
"FullChangeHistory": "Full Change History"
"FullChangeHistory": "Full Change History",
"Permission:RefreshReleaseLogs": "Refresh Release Logs",
"ReleaseLogs": "Release Logs",
"AuthorUserName": "Author User Name"
}
}

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

@ -208,7 +208,7 @@
"WhenShouldIRenewMyLicense": "When should I renew my license?",
"WhenShouldIRenewMyLicenseExplanation": "If you renew your license within <strong>{3} days</strong> after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}. However, if you renew your license after <strong>{3} days</strong> since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"TrialPlan": "Do you have a trial plan?",
"TrialPlanExplanation": "No, there is no trial version for ABP Commercial. You can check the community edition to understand the code quality and approaches. We also offer a 30-day money-back guarantee for the Team license, no questions asked! You can request a refund within the first 30 days. We provide a 60% refund within 30 days for Business and Enterprise licenses. This is because the Business and Enterprise licenses contain the full source-code of all the modules and themes.",
"TrialPlanExplanation": "Yes, to start your free trial contact <a href=\"mailto:marketing@abp.io\">marketing@abp.io</a>. We also offer a 30-day money-back guarantee for the Team license, no questions asked! You can request a full refund within the first 30 days of the license purchase. We provide a 60% refund within 30 days for Business and Enterprise licenses. This is because the Business and Enterprise licenses contain the full source-code of all the modules and themes.",
"DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?",
"DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.<br/>After sending the license fee via bank transfer, send your receipt and requested license type to accounting@volosoft.com.<br/>Our international bank account information:",
"HowToUpgrade": "How to upgrade existing applications when a new version is available?",
@ -231,7 +231,7 @@
"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 (along with any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.",
"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 we provide a 60% refund within 30 days for Business and Enterprise licenses. 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 to your <a href=\"https://secure.2checkout.com/cpanel/login.php\" target=\"_blank\">2Checkout</a> account",
@ -842,6 +842,58 @@
"BlazoriseSupportExplanation3": "Log into the Blazorise support website at <a href=\"https://blazorise.com/support/login\">blazorise.com/support/login</a>.",
"BlazoriseSupportExplanation4": "If you have an active ABP Commercial license, you will also have a Blazorise PRO license. You can get your Blazorise license key at <a href=\"https://blazorise.com/support/user/manage/license\">blazorise.com/support/user/manage/license</a>.",
"BlazoriseSupportExplanation5": "You can post your questions on the support website and generate a product token for your application.",
"AbpLiveTrainingPackages": "ABP Live Training Packages"
"AbpLiveTrainingPackages": "ABP Live Training Packages",
"Releases": "Releases",
"ReleasesDescription": "ABP Commercial releases and their changelogs.",
"ReleaseDate": "Release Date",
"Labels": "Labels",
"PreRelease": "Pre-release",
"AllTypes": "All Types",
"Enhancement": "Enhancement",
"Bug": "Bug",
"Feature": "Feature",
"AllUIs": "All UI's",
"MVC": "MVC",
"HowItWorks_Page_Title": "How it works?",
"HowItWorks_Page_Description": "ABP Framework extends the .NET platform. So, anything you can do with a plain .NET solution is already possible with the ABP Framework. That makes it easy to get started with a low learning curve.",
"HowItWorks": "How it works?",
"HowItWorks_Description1": "ABP Framework extends the .NET platform. So, anything you can do with a plain .NET solution is already possible with the ABP Framework. That makes it easy to get started with a low learning curve.",
"HowItWorks_Description2": "Once you start learning and using the ABP Framework features, developing your software will be much more enjoyable than ever.",
"HowItWorks_Description3": "This page basically explains how you use the ABP.IO Platform as a .NET developer.",
"CreateANewSolution": "Create a New .NET Solution",
"CreateANewSolution_Description1": "Everything starts by creating a new ABP integrated .NET solution.",
"StartWithStartupTemplates": "Start one of the pre-built <a href=\"/startup-templates\">startup solution templates</a>",
"SimpleMonolithApplicationTemplate": "Simple monolith application template",
"LayeredApplicationTemplate": "Layered application template",
"MicroserviceSolutionTemplate": "Microservice solution template",
"CreateEmptySolutionAndUseAbp": "Or create a new empty .NET solution and install ABP NuGet & NPM <a href=\"https://abp.io/packages\" target=\"_blank\">packages</a> yourself.",
"CreatingSolutionWithMultipleOptions": "There are multiple User Interface and Database options while creating a new solution.",
"DatabaseOptions": "Database Options",
"UIOptions": "UI Options",
"DotnetSolutionWithoutDependency": "Now, you have a regular .NET solution in your local computer that has no dependency to a cloud platform or external service.",
"CheckTheDocumentForDetails": "You can check the <a href=\"{0}\" target=\"_blank\">{1}</a> document for details.",
"UIAndDatabaseIndependent": "ABP can work with any UI and any database provider supported by .NET. \n However, these UI and database providers are pre-integrated and well documented.",
"InstallAbpModules": "Install ABP Modules",
"DevelopYourSolution": "Develop Your Solution",
"DeployAnywhere": "Deploy Anywhere",
"InstallAbpModule_Description1": "ABP is a modular application development framework. Startup solution templates already come with the essential modules installed. \n But there are more application modules you may want to use in your solution.",
"InstallAbpModule_Description2": "Every module consists of a few NuGet and NPM packages and has an installation document. <a href=\"/tools/suite\">ABP Suite</a> does most of the work automatically, then you manually configure or fine tune the module based on its documentation.",
"DevelopYourSolution_Description1": "ABP’s infrastructure makes you <b class=\"text-white\">focus on your own business code</b> by automating the repetitive work and providing pre-built infrastructure and application <a href=\"https://abp.io/features\" target=\"_blank\">features</a>.",
"DevelopYourSolution_Description2": "In the following code block, you can see how the ABP Framework seamlessly integrates into your code and automates the repetitive tasks for you.",
"DevelopYourSolution_Description3": "Even in this short code block, ABP does a lot of things for you.",
"DevelopYourSolution_Description4": "It provides base classes to apply conventions, like \n <a href=\"https://docs.abp.io/en/abp/latest/Dependency-Injection\" target=\"_blank\">dependency injection</a>. Generic \n <a href=\"https://docs.abp.io/en/abp/latest/Repositories\" target=\"_blank\">repository</a> services provide a convenient \n way to interact with the database. Declarative \n <a href=\"https://docs.abp.io/en/abp/latest/Authorization\" target=\"_blank\">authorization</a> works with a fine-tuned permission system.",
"DevelopYourSolution_Description5": "ABP completely automates \n <a href=\"https://docs.abp.io/en/abp/latest/Unit-Of-Work\" target=\"_blank\">unit of work</a> (for database connection and transaction management), \n <a href=\"https://docs.abp.io/en/abp/latest/Exception-Handling\" target=\"_blank\">exception handling</a>, \n <a href=\"https://docs.abp.io/en/abp/latest/Validation\" target=\"_blank\">validation</a>\n and <a href=\"https://docs.abp.io/en/abp/latest/Audit-Logging\" target=\"_blank\">audit logging</a>. It provides many more building blocks to simplify your daily development tasks and focus on your own code while creating production ready \n applications.",
"DevelopYourSolution_Description6": "You can imagine how much that code block can be long and complicated if you would do it all manually.",
"SuiteCrudGenerationInFewSeconds": "In addition to hand coding your solution, you can create fully working advanced CRUD pages in a few minutes using the ABP Suite tooling. It generates the code into your solution, so you can fine tune it based on your custom requirements.",
"DeployAnywhere_Description1": "At the end of the day, you have a pure .NET solution. You can deploy your solution to your own server, to a cloud platform, kubernetes or anywhere you want. You can deploy to as many servers as you want. ABP is a deployment environment agnostic tool.",
"ExpertiseAbpFramework": "Expertise the ABP Framework",
"ExpertiseAbpFramework_Description1": "Want to go beyond basics and get expertise with the ABP.IO Platform?",
"FreeDownload": "Free Download",
"Read": "Read",
"HavingTrouble": "Having Trouble?",
"HavingTrouble_Description1": "Do you have problems with developing your solution? We are here! Use the ABP Support platform \n or send an email to get help directly from the Core ABP Framework team members.",
"WeAreHereToHelp_Description1": "You can browse our help topics or search in the frequently asked questions, \n or you can ask us a question by using the contact form.",
"AllModules": "All Modules",
"AllModules_Description1": "Account, Audit Logging, CMS Kit, Forms, GDPR, Identity, Identity Server UI, Language Management, LeptonX Theme and more..."
}
}

2
common.props

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

2
delete-bin-obj.ps1

@ -1,7 +1,5 @@
Clear-Host
Write-Host "Deleting all BIN and OBJ folders..." -ForegroundColor Cyan
Get-ChildItem -Path . -Include bin,obj -Recurse -Directory | ForEach-Object {
if ($_.FullName -notmatch "\\node_modules\\") {
Write-Host "Deleting:" $_.FullName -ForegroundColor Yellow

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/01-microservice-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/02-run-single-service.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/03-tye-yaml-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/04-tye-dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/05-example-microservice-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

BIN
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/06-abp-studio-kubernetes-tunnel-how-it-works.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

226
docs/en/Community-Articles/2023-06-05-Kubernetes-Integration-Abp-Studio/POST.md

@ -0,0 +1,226 @@
# Kubernetes Integrated Microservice Development with .NET and ABP Studio
## Abstract
**Microservice architecture** is a quite common approach to build **highly scalable** solutions with a large development team. While there are standard tools, like **Kubernetes**, to deploy, run and scale your microservices, the **development side** is not as mature as that. It is not easy to develop, run and test a single microservice that **depends on** other microservices and services. **Running** a copy of the entire system in the developer's machine is impractical.
In this article, I will introduce an **efficient** way of creating a **development environment** that is well **integrated to Kubernetes**. In this way, you can just run the microservice you are building and let Kubernetes run all your dependencies in your local machine or a remote server.
I will demonstrate the solution by introducing and using a brand new tool for ABP Developers: **ABP Studio**!
>**This article is based on my *[Kubernetes Integrated Microservice Development with ABP Studio](https://www.youtube.com/watch?v=XiPRcIHJ3NE)* talk performed in [ABP Dotnet Conf'23](https://abp.io/conference). You can watch that talk if you would like watching rather than reading.**
## Topics
Let’s start by introducing the main topics I will cover in this article:
* I will begin by defining the **problem**: Why it is hard to prepare a development environment for a microservice development.
* I will mention and demonstrate how **Microsoft’s Tye project** tries to solve the problem and why it falls short.
* Then I will introduce the **ABP Studio**, a brand new product for .NET developers, which has an efficient solution for **running multi-application solutions** locally.
* Finally, I will offer an efficient way of developing such complex solutions locally by **integrating Kubernetes into our development environment**, and demonstrate how ABP Studio automates it for us.
## Difficulties of a Microservice Development Environment
It is important to understand what is the essential problem when we try to set up a development environment for a microservice solution. To understand the main difficulty, let’s see what a microservice solution looks like:
![example-microservice-solution](01-microservice-solution.png)
A typical microservice solution consists of many components communicating to each other. In this example;
* We have **two applications** for users. One for back-office admin users and one for the end-users of the system, a public website.
* These applications use dedicated **API Gateways** since we are using the Backend For Frontend pattern here.
* There is a separate **authentication service** application to login and manage user accounts.
* Behind the API gateways, we typically have many API applications, called **microservices**. Here, I show only three microservices, but there will be tens of microservices even in a medium-size system. Each of these microservices may have their own databases.
* The microservices typically **interact** with each other using REST API calls or asynchronous messaging through an event bus service, like RabbitMQ here.
There will be much more **supporting services**, like Redis, Elasticsearch and so on. As you see, we have a lot of independent services and applications interacting with each other in a typical microservice solution.
### How to run a single service locally?
Generally, your responsibility is developing **one or a few of these microservices** or applications, not all of them. For example, you may be responsible for developing or testing the Product microservice:
![run-single-service-in-a-microservice-solution](02-run-single-service.png)
All you want to do is to write your code, then run and test your service if it is correctly working. However, to be able to **develop**, **run**, **test** or **debug** the product microservice, all of the service and infrastructure dependencies need to be running and communicating to each other.
Okay, maybe not all, but most of them are needed. Otherwise, you can not open the application UI, login to the system, add products to basket and place an order to see if it is working properly, or you can not **live debug** your service code in case of trouble.
So, how to make the whole system up and running easily to be able to **focus on developing**, testing and debugging the Product microservice?
## The Project Tye
You are not the only one getting trouble to develop, run, test and debug a microservice solution. Microsoft also thought of it and introduced a tool called the [Project Tye](https://github.com/dotnet/tye). With its own words in their GitHub repository:
> Tye is a tool that makes developing, testing, and deploying microservices and distributed applications easier.
Tye can **simplify microservices development** by making it easy to run many services with one command, use dependencies in containers, discover addresses of other services using simple conventions. It can also deploy .NET applications to Kubernetes by automatically creating containers for .NET applications and generating Kubernetes manifests with minimal knowledge or configuration.
The deployment part is not mature and they have to do a lot of work. However, the solution runner part works very well.
Tye project consists of three major components:
* A **YAML file** to configure your services
* A **CLI tool** to run and deploy the solution
* And a **dashboard** to visualize the running services.
### The tye.yaml file
Tye uses a YAML file to configure your solution. It typically contains many service definitions. There are three type of services can be defined in this YAML file:
* You can add a **.NET Project**, so Tye runs it locally with **dotnet run**
* You can add a **container definition**, and Tye runs it using **Docker**
* And an arbitrary **executable application** that can be run using a terminal command
Here, an example content of a `tye.yaml` file:
![tye-yaml-file](03-tye-yaml-file.png)
### The Tye CLI
Once you define your YAML file, Tye can run all the services with a single terminal command: **tye run**. It builds all .NET applications before running them. If you specify the **watch parameter**, it watches the changes in your .NET projects, re-builds and runs them when a change happens. There are also [other commands](https://github.com/dotnet/tye/blob/main/docs/reference/commandline/README.md) to build and deploy your solution.
### The Tye Dashboard
The final major component is the Tye Dashboard. With this dashboard, you can see the **list of the services** with their statuses. You can also view their **logs** and open their **UI** with a single click.
A screenshot from the Tye Dashboard:
![project-tye-dashboard](04-tye-dashboard.png)
### The Example Microservice Solution
I’ve prepared a microservice solution for demos in this article and I will use the same solution in all demos. Here, an overall diagram of the example solution:
![example-microservice-solution](05-example-microservice-solution.png)
We have two web applications, two API Gateways, 5 microservices. 3 of them are shown here: Product, Ordering and Identity microservices. Every microservice has its independent SQL Server database. They communicate through REST API calls and distributed events via RabbitMQ.
### The Project Tye Demo
You can watch my 6-minutes demo to see the Project Tye in action:
<iframe width="560" height="315" src="https://www.youtube.com/embed/S0-z29lMokA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=S0-z29lMokA)
### Tye: Shortcomings
The demo shows how the Tye project is useful. However, there are many missing points and problems with the current project state. Let’s talk about them;
* The first problem is that it **starts very slow**, because every time you run it, it builds all the services. It takes more than 1 minute for a solution with only ten services.
* It currently can run everything in your **local computer**. Even if you want to develop a single service, running tens of services locally consumes your system resources. You need a lot of RAM and CPU if you want to work a large solution locally.
* The **UI part** is currently very simple. For example, if you want to debug one of your services by running it in Visual Studio, you need to stop all the services, change the YAML file to exclude that service from Tye, then run all the services again. Only after that you can run your service in Visual Studio to debug it as integrated to other services. Obviously this process is not easy and comfortable.
* We can only see the application logs in the UI. **More insights** about the internals of services would be very helpful.
* Tye currently has some **partial documents** in its GitHub repository. However, the documentation is far away from being complete.
* Finally, and most importantly, there is **no active development** on the Tye project on GitHub. It was already an experimental project with its early state, as declared in its GitHub repository.
So, basically, it seems Microsoft has no further interest in this project and you can expect it will be retired in the near future.
## ABP Studio
Okay, now we came to the ABP Studio part. At [Volosoft](https://www.volosoft.com/), we were silently working on the ABP Studio project for more than one year.
ABP Studio is a **cross-platform desktop application** for ABP developers. It is **well integrated** to the ABP Framework and aims to provide a comfortable development environment for you by **automating things**, **providing insights** about your solution, making **develop**, **run** and **deploy** your solutions much easier.
We are planning to release a **beta version** in **Q3 of 2023**. You can expect the following features shipping with the initial release:
* You can **create new ABP solutions** and modules easily with a lot of options.
* You can easily **install or uninstall modules** to your solution.
* You can **explore** the fundamental **structures** of your solution or used modules, like entities, repositories, application services, UI pages, HTTP APIs, database tables and much more.
* You can build **multi-module monolith applications** or distributed microservice solutions by easily adding modules and services into your solution.
* You can easily **run multiple applications** and services with a single click, just like I demonstrated with the Project Tye. However, it has much more features.
* You can run your service by **integrating** it into a **Kubernetes** cluster, so you don’t need to run all your dependencies and all other services in your local development environment. This is the essential topic of this talk and I will later demonstrate how it works.
## ABP Studio Solution Runner
I've prepared a demo of the ABP Studio Solution Runner, but first I want to mention the main features of the solution runner:
* First of all, it can run one, **multiple** or **all services** with a single click. In this way, it is very easy to stop a service, run it in Visual Studio to **test** or **debug**.
* All the services are **connected to ABP Studio** and send their internal data to **visualize** on the ABP Studio UI.
* In the **overall view**, you can see a list of services, view **real-time** HTTP Request and exception counts for each service.
* You can see all details of all **HTTP requests** coming to any service.
* You can see **exception details** as real-time in any service, easily filter and search.
* Just like Tye, you can see the **application logs**. But as more, you can filter logs by log level or search in the log texts.
* Last but not least, ABP Studio has an **integrated Chrome browser** inside it. You can browse the UI of your application without leaving the solution runner.
### ABP Studio Solution Runner Demo
You can watch my 9-minutes demo to see ABP Studio Solution Runner in action:
<iframe width="560" height="315" src="https://www.youtube.com/embed/sSCxyccoHqE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=sSCxyccoHqE)
## ABP Studio Kubernetes Tunnel
Finally, we came to the essential topic I want to talk about. Until that point, we had a good understanding of the problem and possible solutions. In this part, we will make a final touch to the solution to have **a great development environment for a microservice solution**.
ABP Studio Kubernetes Tunnel System allows you to **connect your local development environment to a local or remote Kubernetes cluster**, where that cluster already runs your microservice solution. In this way, you can access any service in Kubernetes with their service name as DNS, just like they are running in your local computer. This is established with a secure VPN connection.
Secondly, you can **intercept any service** in that cluster, so all the **traffic to the intercepted service is automatically redirected to your service** that is running in your local machine. When your service needs to use any service in Kubernetes, the traffic is redirected back to the cluster, just like your local service is running inside the Kubernetes. In this way, you don’t need to care about how all other services are configured and running. You just focus on the service you are responsible for developing, testing or debugging. You can use your favorite IDE since you are running your service in your local machine as you always do.
The solution runner is a great way of running multiple services locally. However, if your solution consists of hundreds of services, running all them in your local machine will consume your system resources and slow down your development speed. The best news is that: You can use the **Kubernetes Tunnelling combined with all the solution runner features** to have a perfect local microservice development environment.
### How ABP Studio Kubernetes Tunnel works
I am sure that you want to see it in action, but before that, let me explain how the solution works.
![abp-studio-kubernetes-tunnel-how-it-works](06-abp-studio-kubernetes-tunnel-how-it-works.png)
* **Kubernetes cluster** is shown on the right side and your **local development machine** is shown on the left side.
* As you know, when a **user requests a web page** from your web application, the request is accepted by an **Ingress Controller** inside your Kubernetes cluster.
* The Ingress controller forwards the request to your **web application**, which then uses an **API gateway** to consume your microservices.
* Assume that we have the **Product**, **Ordering** and **Identity** microservices.
* These microservices have their own SQL Server **database** and they are also using **RabbitMQ**, **Redis** and some other **infrastructure services**.
* On the other hand, we want to develop, run or test our **Product microservice** in our **own laptop** using our favorite IDE, let’s say using Visual Studio.
* If we don’t make anything special, the **product microservice in the Kubernetes cluster** will be used by the users, as you can expect. Even if you can somehow run the product microservice in your local computer, the Kubernetes system won’t have any knowledge about it. They are in different systems.
* At this point, ABP Studio comes into play. When you **connect** to the Kubernetes cluster with ABP Studio, it first installs a **VPN server** into your cluster and a **VPN client** into your local machine.
* Then it establishes a secure **VPN tunnel** between your computer and the target Kubernetes cluster. In this way, you can access all services in Kubernetes with their internal cluster IP addresses.
* ABP Studio also installs the **ABP Studio Client Proxy Server** pod into the Kubernetes cluster, and the **ABP Studio Proxy Client** into the developer machine. It is used to collect data from the services and show them in the ABP Studio UI, like HTTP Requests, exceptions, or logs as we’ve seen in the solution runner before.
* Finally, ABP Studio also adds **DNS records to your hosts file**, so you can access to internal Kubernetes services directly with their service names, in addition to their IP addresses.
All of these happen when you click the Connect button on the ABP Studio. Now, your computer can use the **internals of the Kubernetes cluster**. But, how the HTTP Requests coming to the Product service in the Kubernetes cluster are **redirected to your local machine**?
* When you want to run and test a service locally, you **intercept the matched service** in the Kubernetes cluster. When you Intercept a service, ABP Studio makes some more changes in your local computer and in your Kubernetes cluster.
* As first, it installs a **pod** and a **service** into the Kubernetes cluster to intercept the requests coming to the Product microservice and **proxies the requests to** a client application in **your local computer**.
* The local client application then **forwards requests** to the Product microservice instance you are running in your local machine. In this way, whenever the Product service is used in Kubernetes, your local product service is executed. You can easily run, test or debug your service as you normally do with your standard IDE, for example with Visual Studio.
* What happens when your local product service needs to **access** the SQL Server database or RabbitMQ, or another microservice in the Kubernetes cluster? It can **directly use** them just like local services, through the **VPN tunnel** we have already created.
* ABP Studio makes a final change in your local product microservice project: It overrides all the **environment variables** with the ones obtained from the product service in the Kubernetes pod. In this way, your local product service instance feels itself in the Kubernetes cluster. Database connection strings and all other settings will be the same with the Kubernetes environment.
Don’t worry, ABP Studio **doesn’t change your local environment variables**. The change only affects the live product service instance, and it is **reverted** when you disable the interception in ABP Studio. Also, when you click the **Disconnect** button in ABP Studio, all the changes made to your cluster and your local computer will be cleared. It won’t leave any sign in your systems.
### ABP Studio Kubernetes Tunnel Demo
So, now we have a fully Kubernetes integrated development environment. Let’s see how it is used in ABP Studio in the following 9-minutes demo:
<iframe width="560" height="315" src="https://www.youtube.com/embed/CeUq2ysz-mQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
[Click here to watch the demo on YouTube](https://www.youtube.com/watch?v=CeUq2ysz-mQ)
## ABP Studio Roadmap
In the next months, the team will be working on the following items:
* Our first goal is to **finalize and stabilize** the current features. We will also **document** how to use them. Only after that, we will open it to customers as a beta version.
* We’ve focused on the development part until now. We also want to add some **deployment** capabilities to ABP Studio in the future.
* Another goal is to make **module customizations** much easier than current by the help of ABP Studio. With ABP Studio, we have completely re-though how to discover and install modules. Based on that, we will also work on a common place to publish and consume application modules for the ABP Framework.
We have other great plans for that product. However, I can not declare them yet as they are secrets for now :) We’ve planned to release the first beta version in **Q3 of 2023**.
I want to notice that ABP Studio is not a new version or replacement of **ABP Suite**. We built the ABP Studio from scratch. Both products will be developed and shipped in parallel. ABP Suite will continue to focus on code generation. ABP Studio doesn’t have any code generation capabilities yet. So, you will use both of them together.
## Summary
Okay let’s summarize what I’ve explained in this article:
* First, I explained **why it is hard to set up a microservice development environment**. We’ve talked about the hardness of developing, running and testing a single microservice that depends on infrastructure services and other microservices.
* I’ve introduced **Project Tye**, a project developed by Microsoft to allow you to easily run multi-application solutions. We made a demo, then discussed its missing points.
* Then I **introduced ABP Studio**, a new product by Volosoft for ABP developers.
As the first important feature, I explained the ABP Studio **Solution Runner** and demonstrated how it works and how you can develop and debug your services.
* Finally, I introduced ABP Studio’s **Kubernetes Tunneling** feature. First, I explained how it works with a complicated diagram. Then I made a short demo to see it in action.
With that final part, we had a **great and easy to use microservice development environment**. It is great, because we can focus on only a single service, while we don’t care about all other parts of the solution. Also, we can save our local development machine system resources by delegating running the dependencies to a remote Kubernetes server.
## See Also
* ABP Dotnet Conf'23 Talk on YouTube: [Kubernetes Integrated Microservice Development with ABP Studio](https://www.youtube.com/watch?v=XiPRcIHJ3NE)

28
docs/en/Migration-Guides/Abp-7_3.md

@ -0,0 +1,28 @@
# ABP Version 7.3 Migration Guide
This document is a guide for upgrading ABP v7.2 solutions to ABP v7.3. There are a few changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application.
## OpenIddict - Refactoring of `ClaimsPrincipal`
There are some changes that you might need to fix in your code. You can see the following list of the required changes:
* `AbpOpenIddictClaimDestinationsManager` was renamed as `AbpOpenIddictClaimsPrincipalManager`.
* Use `AbpOpenIddictClaimsPrincipalManager.HandleAsync` instead of `AbpOpenIddictClaimDestinationsManager.SetAsync`, which is removed.
* `AbpDefaultOpenIddictClaimDestinationsProvider` was renamed as `AbpDefaultOpenIddictClaimsPrincipalHandler`.
* `IAbpOpenIddictClaimDestinationsProvider` was renamed as `IAbpOpenIddictClaimsPrincipalHandler`.
* Use `IAbpOpenIddictClaimsPrincipalHandler.HandleAsync` instead of `IAbpOpenIddictClaimDestinationsProvider.SetAsync`, which is removed.
* `AbpOpenIddictClaimDestinationsOptions` was renamed as `AbpOpenIddictClaimsPrincipalOptions`.
Please check [this PR](https://github.com/abpframework/abp/pull/16537) if you encounter any problems related to OpenIddict Module.
## Nonce attribute support for Content Security Policy (CSP)
ABP Framework supports adding unique value to nonce attribute for script tags which can be used by Content Security Policy to determine whether or not a given fetch will be allowed to proceed for a given element. In other words, it provides a mechanism to execute only correct script tags with the correct nonce value.
> See the [Security Headers](../UI/AspNetCore/Security-Headers.md) documentation for more information.
This feature comes with a small restriction. If you use any C# code used inside the script tag, it may cause errors (Because a new `NonceScriptTagHelper` has been added, and it replaces script tags in the HTML contents).
For example, `<script @string.Empty></script>` will no longer work. However, you can use the C# code for an attribute of script tag, for example, `<script src="@string.Empty"></script>` is completely valid and won't cause any problem.
> Note: You should not use any C# code used inside the script tag, even if you don't use this feature. Because it might cause errors.

1
docs/en/Migration-Guides/Index.md

@ -2,6 +2,7 @@
The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../Upgrading.md).
- [7.2 to 7.3](Abp-7_3.md)
- [7.1 to 7.2](Abp-7_2.md)
- [7.0 to 7.1](Abp-7_1.md)
- [6.0 to 7.0](Abp-7_0.md)

2
docs/en/UI/Angular/Authorization.md

@ -32,7 +32,7 @@ According to this flow, the user is redirected to an external login page which i
### Resource Owner Password Flow
If you implemented the [Angular UI account module](./Account-Module) to your project, you can switch the flow to resource owner password flow by changing the OAuth configuration in the _environment.ts_ files as shown below:
If you have used the [Angular UI account module](./Account-Module) in your project, you can switch to the resource owner password flow by changing the OAuth configuration in the _environment.ts_ files as shown below:
```js
import { Config } from '@abp/ng.core';

23
docs/en/UI/AspNetCore/Security-Headers.md

@ -74,3 +74,26 @@ Configure<AbpSecurityHeadersOptions>(options =>
});
});
```
### Ignore Abp Security Headers
You can ignore the Abp Security Headers for some actions or pages. You can use the `IgnoreAbpSecurityHeaderAttribute` attribute for this.
**Example:**
```csharp
@using Volo.Abp.AspNetCore.Security
@attribute [IgnoreAbpSecurityHeaderAttribute]
```
**Example:**
```csharp
[IgnoreAbpSecurityHeaderAttribute]
public class IndexModel : AbpPageModel
{
public void OnGet()
{
}
}
```

12
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs

@ -33,11 +33,19 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency
var requestAcceptTypeHtml = context.Request.Headers["Accept"].Any(x =>
x.Contains("text/html") || x.Contains("*/*") || x.Contains("application/xhtml+xml"));
var endpoint = context.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<IgnoreAbpSecurityHeaderAttribute>() != null)
{
await next.Invoke(context);
return;
}
if (!requestAcceptTypeHtml
|| !Options.Value.UseContentSecurityPolicyHeader
|| await AlwaysIgnoreContentTypes(context)
|| context.GetEndpoint() == null
|| endpoint == null
|| Options.Value.IgnoredScriptNoncePaths.Any(x => context.Request.Path.StartsWithSegments(x.EnsureStartsWith('/'))))
{
AddOtherHeaders(context);
@ -111,7 +119,7 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency
var nonceStr = $" 'nonce-{nonceValue}'";
var scriptSrcValue = Options.Value.ContentSecurityPolicyValue.Split(';')
var scriptSrcValue = Options.Value.ContentSecurityPolicyValue?.Split(';')
.FirstOrDefault(x => x.Trim().StartsWith(ScriptSrcKey))?.Trim();
if (scriptSrcValue.IsNullOrWhiteSpace())

9
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/IgnoreAbpSecurityHeader.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.AspNetCore.Security;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class IgnoreAbpSecurityHeaderAttribute : Attribute
{
}

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppNoLayersTemplateBase.cs

@ -26,6 +26,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
{
case DatabaseProvider.NotSpecified:
case DatabaseProvider.EntityFrameworkCore:
context.Symbols.Add("EFCORE");
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Mvc.Mongo"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Host.Mongo"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server.Mongo"));
@ -50,6 +51,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
switch (context.BuildArgs.UiFramework)
{
case UiFramework.Angular:
context.Symbols.Add("ui:angular");
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Mvc"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server"));
steps.Add(new ProjectRenameStep("MyCompanyName.MyProjectName.Host", "MyCompanyName.MyProjectName"));
@ -65,6 +67,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
break;
case UiFramework.Blazor:
context.Symbols.Add("ui:blazor");
steps.Add(new RemoveFolderStep("/angular"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Mvc"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server"));
@ -83,6 +86,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
break;
case UiFramework.BlazorServer:
context.Symbols.Add("ui:blazor-server");
steps.Add(new RemoveFolderStep("/angular"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Mvc"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Host"));
@ -92,6 +96,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
case UiFramework.NotSpecified:
case UiFramework.Mvc:
context.Symbols.Add("ui:mvc");
steps.Add(new RemoveFolderStep("/angular"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Host"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server"));

2
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/JsonAuditSerializer_Test.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
@ -58,7 +57,6 @@ public class JsonAuditSerializer_Test : AbpAuditingTestBase
[DisableAuditing]
public string Password { get; set; }
[JsonIgnore]
public string PrivateEmail { get; set; }
public DateTime Birthday { get; set; }

68
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml

@ -147,27 +147,32 @@
}
</div>
<div class="col ps-1">
<h5 class="mt-2 mb-1">
<a href="/Members/@Model.Post.Writer.UserName">
@if (Model.Post.Writer.Name != null && Model.Post.Writer.Surname != null)
{
<p class="fw-bold pt-2 fs-5">@Model.Post.Writer.Name @Model.Post.Writer.Surname</p>
}
else
@if (Model.Post.Writer != null)
{
<h5 class="mt-2 mb-1">
<a href="/Members/@Model.Post.Writer.UserName">
@if (Model.Post.Writer.Name != null && Model.Post.Writer.Surname != null)
{
<p class="fw-bold pt-2 fs-5">@Model.Post.Writer.Name @Model.Post.Writer.Surname</p>
}
else
{
<p class="fw-bold pt-2 fs-5">@Model.Post.Writer.UserName</p>
}
</a>
</h5>
<div class="position-relative">
@if (Model.Post.Writer.JobTitle != null)
{
<p class="fw-bold pt-2 fs-5">@Model.Post.Writer.UserName</p>
<p class="fw-lighter">@Model.Post.Writer.JobTitle</p>
}
</a>
</h5>
<div class="position-relative">
@if (Model.Post.Writer.JobTitle != null)
{
<p class="fw-lighter">@Model.Post.Writer.JobTitle</p>
}
</div>
@if (CurrentUser.UserName == Model.Post.Writer.UserName)
</div>
}
@if (CurrentUser.Id.HasValue && CurrentUser.Id.Value == Model.Post.Writer?.Id)
{
<a class="fw-lighter" href="/Members/@Model.Post.Writer.UserName#edit-profile">@L["EditProfile"] <i class="fas fa-edit"></i></a>
<a class="fw-lighter" href="/Members/@Model.Post.Writer?.UserName#edit-profile">@L["EditProfile"] <i class="fas fa-edit"></i></a>
}
</div>
@if (Model.LatestPosts.Count > 1)
@ -185,23 +190,24 @@
<div class="article-owner">
<div class="article-infos">
<div class="user-card pt-3">
<div class="row">
<div class="col-auto pe-1">
@if (post.Writer != null)
{
@if (post.Writer != null)
{
<div class="row">
<div class="col-auto pe-1">
<a href="/Members/@post.Writer.UserName" aria-label="Go to user profile">
<img gravatar-email="@post.Writer.Email" default-image="Identicon" class="last-post-image" alt="User Avatar"/>
</a>
}
</div>
<div class="col">
<h5 class="last-post-name">
<a href="/Members/@post.Writer.UserName">
@(post.Writer.UserName)
</a>
</h5>
</div>
<div class="col">
<h5 class="last-post-name">
<a href="/Members/@post.Writer!.UserName">
@(post.Writer!.UserName)
</a>
</h5>
</div>
</div>
</div>
}
</div>
</div>
<div class="col mt-2">

6
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml.cs

@ -86,7 +86,11 @@ namespace Volo.Blogging.Pages.Blog.Posts
{
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl });
PostsList = await _postAppService.GetListByUserIdAsync(Post.Writer.Id);
PostsList = Post.Writer != null
? await _postAppService.GetListByUserIdAsync(Post.Writer.Id)
: new List<PostWithDetailsDto>();
LatestPosts = await _postAppService.GetLatestBlogPostsAsync(Blog.Id, 5);
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(Post.Id);
CountComments();

7
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDefinitionRecord.cs

@ -7,13 +7,6 @@ namespace Volo.Abp.FeatureManagement;
public class FeatureDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if feature definitions have changed (in StaticFeatureSaver.CalculateHash()).
*/
[JsonIgnore] //TODO: TODO: Use JSON modifier to ignore this property
public override Guid Id { get; protected set; }
public string GroupName { get; set; }
public string Name { get; set; }

8
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureGroupDefinitionRecord.cs

@ -1,5 +1,4 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
@ -7,13 +6,6 @@ namespace Volo.Abp.FeatureManagement;
public class FeatureGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if feature definitions have changed (in StaticFeatureSaver.CalculateHash()).
*/
[JsonIgnore] //TODO: TODO: Use JSON modifier to ignore this property
public override Guid Id { get; protected set; }
public string Name { get; set; }
public string DisplayName { get; set; }

18
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureSaver.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
@ -10,6 +11,7 @@ using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Features;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
@ -297,13 +299,25 @@ public class StaticFeatureSaver : IStaticFeatureSaver, ITransientDependency
IEnumerable<string> deletedFeatureGroups,
IEnumerable<string> deletedFeatures)
{
var jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
new AbpIgnorePropertiesModifiers<FeatureGroupDefinitionRecord, Guid>().CreateModifyAction(x => x.Id),
new AbpIgnorePropertiesModifiers<FeatureDefinitionRecord, Guid>().CreateModifyAction(x => x.Id)
}
}
};
var stringBuilder = new StringBuilder();
stringBuilder.Append("FeatureGroupRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(featureGroupRecords));
stringBuilder.AppendLine(JsonSerializer.Serialize(featureGroupRecords, jsonSerializerOptions));
stringBuilder.Append("FeatureRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(featureRecords));
stringBuilder.AppendLine(JsonSerializer.Serialize(featureRecords, jsonSerializerOptions));
stringBuilder.Append("DeletedFeatureGroups:");
stringBuilder.AppendLine(deletedFeatureGroups.JoinAsString(","));

47
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/CalculateHash_Tests.cs

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using Shouldly;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Xunit;
namespace Volo.Abp.FeatureManagement;
public class CalculateHash_Tests : FeatureManagementDomainTestBase
{
[Fact]
public void Test()
{
var jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
new AbpIgnorePropertiesModifiers<FeatureGroupDefinitionRecord, Guid>().CreateModifyAction(x => x.Id),
new AbpIgnorePropertiesModifiers<FeatureDefinitionRecord, Guid>().CreateModifyAction(x => x.Id)
}
}
};
var id = Guid.NewGuid();
var json = JsonSerializer.Serialize(new List<FeatureGroupDefinitionRecord>()
{
new FeatureGroupDefinitionRecord(id, "Test", "Test")
},
jsonSerializerOptions);
json.ShouldNotContain("\"Id\"");
json.ShouldNotContain(id.ToString("D"));
json = JsonSerializer.Serialize(new List<FeatureDefinitionRecord>()
{
new FeatureDefinitionRecord(id, "Test", "Test", "Test", "Test", "Test", "Test", true, true,"Test", "Test")
},
jsonSerializerOptions);
json.ShouldNotContain("\"Id\"");
json.ShouldNotContain(id.ToString("D"));
}
}

2
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using Volo.Abp.AspNetCore.Security;
using Volo.Abp.OpenIddict.ViewModels.Authorization;
namespace Volo.Abp.OpenIddict.Controllers;
@ -20,6 +21,7 @@ public class AuthorizeController : AbpOpenIdDictControllerBase
{
[HttpGet, HttpPost]
[IgnoreAntiforgeryToken]
[IgnoreAbpSecurityHeader]
public virtual async Task<IActionResult> HandleAsync()
{
var request = await GetOpenIddictServerRequestAsync(HttpContext);

54
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs

@ -1,5 +1,4 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
@ -8,25 +7,18 @@ namespace Volo.Abp.PermissionManagement;
public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if permission definitions have changed (in StaticPermissionSaver.CalculateHash()).
*/
[JsonIgnore]
public override Guid Id { get; protected set; }
public string GroupName { get; set; }
public string Name { get; set; }
public string ParentName { get; set; }
public string DisplayName { get; set; }
public bool IsEnabled { get; set; }
public MultiTenancySides MultiTenancySide { get; set; }
/// <summary>
/// Comma separated list of provider names.
/// </summary>
@ -36,7 +28,7 @@ public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraPro
/// Serialized string to store info about the state checkers.
/// </summary>
public string StateCheckers { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public PermissionDefinitionRecord()
@ -44,7 +36,7 @@ public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraPro
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public PermissionDefinitionRecord(
Guid id,
string groupName,
@ -76,37 +68,37 @@ public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraPro
{
return false;
}
if (GroupName != otherRecord.GroupName)
{
return false;
}
if (ParentName != otherRecord.ParentName)
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
}
if (IsEnabled != otherRecord.IsEnabled)
{
return false;
}
if (MultiTenancySide != otherRecord.MultiTenancySide)
{
return false;
}
if (Providers != otherRecord.Providers)
{
return false;
}
if (StateCheckers != otherRecord.StateCheckers)
{
return false;
@ -126,37 +118,37 @@ public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraPro
{
Name = otherRecord.Name;
}
if (GroupName != otherRecord.GroupName)
{
GroupName = otherRecord.GroupName;
}
if (ParentName != otherRecord.ParentName)
{
ParentName = otherRecord.ParentName;
}
if (DisplayName != otherRecord.DisplayName)
{
DisplayName = otherRecord.DisplayName;
}
if (IsEnabled != otherRecord.IsEnabled)
{
IsEnabled = otherRecord.IsEnabled;
}
if (MultiTenancySide != otherRecord.MultiTenancySide)
{
MultiTenancySide = otherRecord.MultiTenancySide;
}
if (Providers != otherRecord.Providers)
{
Providers = otherRecord.Providers;
}
if (StateCheckers != otherRecord.StateCheckers)
{
StateCheckers = otherRecord.StateCheckers;
@ -165,11 +157,11 @@ public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraPro
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}
}

22
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecord.cs

@ -1,5 +1,4 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
@ -7,17 +6,10 @@ namespace Volo.Abp.PermissionManagement;
public class PermissionGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if permission definitions have changed (in StaticPermissionSaver.CalculateHash()).
*/
[JsonIgnore]
public override Guid Id { get; protected set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public PermissionGroupDefinitionRecord()
@ -25,7 +17,7 @@ public class PermissionGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExt
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public PermissionGroupDefinitionRecord(
Guid id,
string name,
@ -45,7 +37,7 @@ public class PermissionGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExt
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
@ -70,15 +62,15 @@ public class PermissionGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExt
{
DisplayName = otherRecord.DisplayName;
}
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}
}

18
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
@ -10,6 +11,7 @@ using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
@ -298,13 +300,25 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc
IEnumerable<string> deletedPermissionGroups,
IEnumerable<string> deletedPermissions)
{
var jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
new AbpIgnorePropertiesModifiers<PermissionGroupDefinitionRecord, Guid>().CreateModifyAction(x => x.Id),
new AbpIgnorePropertiesModifiers<PermissionDefinitionRecord, Guid>().CreateModifyAction(x => x.Id)
}
}
};
var stringBuilder = new StringBuilder();
stringBuilder.Append("PermissionGroupRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionGroupRecords));
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionGroupRecords, jsonSerializerOptions));
stringBuilder.Append("PermissionRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionRecords));
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionRecords, jsonSerializerOptions));
stringBuilder.Append("DeletedPermissionGroups:");
stringBuilder.AppendLine(deletedPermissionGroups.JoinAsString(","));

43
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using Shouldly;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Xunit;
namespace Volo.Abp.PermissionManagement;
public class CalculateHash_Tests: PermissionTestBase
{
[Fact]
public void Test()
{
var jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
new AbpIgnorePropertiesModifiers<PermissionGroupDefinitionRecord, Guid>().CreateModifyAction(x => x.Id),
new AbpIgnorePropertiesModifiers<PermissionDefinitionRecord, Guid>().CreateModifyAction(x => x.Id)
}
}
};
var id = Guid.NewGuid();
var json = JsonSerializer.Serialize(new List<PermissionGroupDefinitionRecord>()
{
new PermissionGroupDefinitionRecord(id, "Test", "Test")
},
jsonSerializerOptions);
json.ShouldNotContain("\"Id\"");
json.ShouldNotContain(id.ToString("D"));
json = JsonSerializer.Serialize(new List<PermissionDefinitionRecord>()
{
new PermissionDefinitionRecord(id, "Test", "Test", "Test", "Test")
},
jsonSerializerOptions);
json.ShouldNotContain("\"Id\"");
json.ShouldNotContain(id.ToString("D"));
}
}

2
npm/lerna.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"packages": [
"packs/*"
],

30
npm/ng-packs/package.json

@ -41,24 +41,23 @@
"migrate-nx": "yarn nx migrate --run-migrations",
"copy-to:app": "cd scripts && yarn && yarn copy-to-templates -t app",
"update-version": "nx generate @abp/nx.generators:update-version"
},
"private": true,
"devDependencies": {
"@abp/ng.account": "~7.2.1",
"@abp/ng.account.core": "~7.2.1",
"@abp/ng.core": "~7.2.1",
"@abp/ng.feature-management": "~7.2.1",
"@abp/ng.identity": "~7.2.1",
"@abp/ng.oauth": "~7.2.1",
"@abp/ng.permission-management": "~7.2.1",
"@abp/ng.schematics": "~7.2.1",
"@abp/ng.setting-management": "~7.2.1",
"@abp/ng.tenant-management": "~7.2.1",
"@abp/ng.theme.basic": "~7.2.1",
"@abp/ng.account": "~7.3.0-rc.1",
"@abp/ng.account.core": "~7.3.0-rc.1",
"@abp/ng.core": "~7.3.0-rc.1",
"@abp/ng.feature-management": "~7.3.0-rc.1",
"@abp/ng.identity": "~7.3.0-rc.1",
"@abp/ng.oauth": "~7.3.0-rc.1",
"@abp/ng.permission-management": "~7.3.0-rc.1",
"@abp/ng.schematics": "~7.3.0-rc.1",
"@abp/ng.setting-management": "~7.3.0-rc.1",
"@abp/ng.tenant-management": "~7.3.0-rc.1",
"@abp/ng.theme.basic": "~7.3.0-rc.1",
"@abp/ng.theme.lepton-x": "^2.2.0",
"@abp/ng.theme.shared": "~7.2.1",
"@abp/utils": "~7.2.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"@abp/utils": "~7.3.0-rc.1",
"@angular-devkit/build-angular": "16.0.1",
"@angular-devkit/core": "16.0.1",
"@angular-devkit/schematics": "16.0.1",
@ -120,7 +119,7 @@
"lerna": "^4.0.0",
"lint-staged": "^13.0.3",
"ng-packagr": "16.0.1",
"ng-zorro-antd": "^15.0.0",
"ng-zorro-antd": "^16.0.0",
"nx": "16.2.2",
"perfect-scrollbar": "^1.5.5",
"postcss": "^8.3.9",
@ -149,4 +148,3 @@
},
"dependencies": {}
}

6
npm/ng-packs/packages/account-core/package.json

@ -1,14 +1,14 @@
{
"name": "@abp/ng.account.core",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"peerDependencies": {
"@abp/ng.core": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.core": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0"
},

6
npm/ng-packs/packages/account/package.json

@ -1,14 +1,14 @@
{
"name": "@abp/ng.account",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.account.core": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.account.core": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

19
npm/ng-packs/packages/account/src/lib/account-routing.module.ts

@ -1,20 +1,22 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, mapToCanActivate } from '@angular/router';
import {
AuthGuard,
ReplaceableComponents,
ReplaceableRouteContainerComponent,
RouterOutletComponent,
} from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component';
import { LoginComponent } from './components/login/login.component';
import { ManageProfileComponent } from './components/manage-profile/manage-profile.component';
import { RegisterComponent } from './components/register/register.component';
import { ResetPasswordComponent } from './components/reset-password/reset-password.component';
import { eAccountComponents } from './enums/components';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
import { AccountExtensionsGuard } from './guards';
import { AccountExtensionsGuard, AuthenticationFlowGuard } from './guards';
const canActivate = mapToCanActivate([AuthenticationFlowGuard]);
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'login' },
@ -25,7 +27,7 @@ const routes: Routes = [
{
path: 'login',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthenticationFlowGuard],
canActivate,
data: {
replaceableComponent: {
key: eAccountComponents.Login,
@ -36,7 +38,7 @@ const routes: Routes = [
{
path: 'register',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthenticationFlowGuard],
canActivate,
data: {
replaceableComponent: {
key: eAccountComponents.Register,
@ -47,7 +49,8 @@ const routes: Routes = [
{
path: 'forgot-password',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthenticationFlowGuard],
canActivate,
data: {
replaceableComponent: {
key: eAccountComponents.ForgotPassword,
@ -70,7 +73,7 @@ const routes: Routes = [
{
path: 'manage',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthGuard, AccountExtensionsGuard],
canActivate: mapToCanActivate([AuthGuard, AccountExtensionsGuard]),
data: {
replaceableComponent: {
key: eAccountComponents.ManageProfile,

9
npm/ng-packs/packages/account/src/lib/guards/authentication-flow.guard.ts

@ -1,10 +1,9 @@
import { AuthService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { AuthService, IAbpGuard } from '@abp/ng.core';
import { Injectable, inject } from '@angular/core';
@Injectable()
export class AuthenticationFlowGuard {
constructor(private authService: AuthService) {}
export class AuthenticationFlowGuard implements IAbpGuard {
protected readonly authService = inject(AuthService);
canActivate() {
if (this.authService.isInternalAuth) return true;

28
npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts

@ -1,46 +1,46 @@
import { Injectable, Injector } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { tap, map } from 'rxjs/operators';
import { ConfigStateService, IAbpGuard } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
mapEntitiesToContributors,
mergeWithDefaultProps,
} from '@abp/ng.theme.shared/extensions';
import { ConfigStateService } from '@abp/ng.core';
import { tap, map, mapTo } from 'rxjs/operators';
import {
ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS,
DEFAULT_ACCOUNT_FORM_PROPS,
} from '../tokens/extensions.token';
import { AccountEditFormPropContributors } from '../models/config-options';
import { eAccountComponents } from '../enums/components';
@Injectable()
export class AccountExtensionsGuard {
constructor(private injector: Injector) {}
export class AccountExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
const extensions: ExtensionsService = this.injector.get(ExtensionsService);
const config = { optional: true };
const editFormContributors: AccountEditFormPropContributors =
this.injector.get(ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, null) || {};
const editFormContributors = inject(ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
const configState = this.injector.get(ConfigStateService);
return getObjectExtensionEntitiesFromStore(configState, 'Identity').pipe(
return getObjectExtensionEntitiesFromStore(this.configState, 'Identity').pipe(
map(entities => ({
[eAccountComponents.PersonalSettings]: entities.User,
})),
mapEntitiesToContributors(configState, 'AbpIdentity'),
mapEntitiesToContributors(this.configState, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultProps(
extensions.editFormProps,
this.extensions.editFormProps,
DEFAULT_ACCOUNT_FORM_PROPS,
objectExtensionContributors.editForm,
editFormContributors,
);
}),
mapTo(true),
map(() => true),
);
}
}

8
npm/ng-packs/packages/components/package.json

@ -1,18 +1,18 @@
{
"name": "@abp/ng.components",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"peerDependencies": {
"@abp/ng.core": ">=7.0.3",
"@abp/ng.theme.shared": ">=7.0.3"
"@abp/ng.core": ">=7.3.0-rc.1",
"@abp/ng.theme.shared": ">=7.3.0-rc.1"
},
"dependencies": {
"chart.js": "^3.5.1",
"ng-zorro-antd": "^15.0.0",
"ng-zorro-antd": "^16.0.0",
"tslib": "^2.0.0"
},
"publishConfig": {

4
npm/ng-packs/packages/core/package.json

@ -1,13 +1,13 @@
{
"name": "@abp/ng.core",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/utils": "~7.2.2",
"@abp/utils": "~7.3.0-rc.1",
"angular-oauth2-oidc": "^15.0.1",
"just-clone": "^6.1.1",
"just-compare": "^2.3.0",

9
npm/ng-packs/packages/core/src/lib/abstracts/abstract-guard.ts

@ -0,0 +1,9 @@
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
export interface IAbpGuard {
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}

7
npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts

@ -1,13 +1,14 @@
import { Injectable } from '@angular/core';
import { UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import { IAbpGuard } from './abstract-guard';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements IAuthGuard {
export class AuthGuard implements IAbpGuard {
canActivate(): Observable<boolean> | boolean | UrlTree {
console.error('You should add @abp/ng-oauth packages or create your own auth packages.');
return false;
}
}
export interface IAuthGuard {}

1
npm/ng-packs/packages/core/src/lib/abstracts/index.ts

@ -1,3 +1,4 @@
export * from './abstract-guard';
export * from './ng-model.component';
export * from './auth.guard';
export * from './auth.service';

28
npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts

@ -1,23 +1,25 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { HttpErrorReporterService } from '../services/http-error-reporter.service';
import { PermissionService } from '../services/permission.service';
import { RoutesService } from '../services/routes.service';
import { OAuthService } from 'angular-oauth2-oidc';
import { IAbpGuard } from '../abstracts';
import { findRoute, getRoutePath } from '../utils/route-utils';
import { RoutesService, PermissionService, HttpErrorReporterService } from '../services';
@Injectable({
providedIn: 'root',
})
export class PermissionGuard {
constructor(
private router: Router,
private routesService: RoutesService,
private permissionService: PermissionService,
private httpErrorReporter: HttpErrorReporterService,
) {}
export class PermissionGuard implements IAbpGuard {
protected readonly router = inject(Router);
protected readonly routesService = inject(RoutesService);
protected readonly oAuthService = inject(OAuthService);
protected readonly permissionService = inject(PermissionService);
protected readonly httpErrorReporter = inject(HttpErrorReporterService);
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
let { requiredPolicy } = route.data || {};
@ -31,7 +33,7 @@ export class PermissionGuard {
return this.permissionService.getGrantedPolicy$(requiredPolicy).pipe(
tap(access => {
if (!access) {
if (!access && this.oAuthService.hasValidAccessToken()) {
this.httpErrorReporter.reportError({ status: 403 } as HttpErrorResponse);
}
}),

4
npm/ng-packs/packages/feature-management/package.json

@ -1,13 +1,13 @@
{
"name": "@abp/ng.feature-management",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

12
npm/ng-packs/packages/generators/package.json

@ -1,6 +1,14 @@
{
"name": "@abp/nx.generators",
"version": "7.3.0-preview20230530",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"generators": "./generators.json",
"type": "commonjs",
"generators": "./generators.json"
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"publishConfig": {
"access": "public"
}
}

8
npm/ng-packs/packages/identity/package.json

@ -1,15 +1,15 @@
{
"name": "@abp/ng.identity",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.components": "~7.2.2",
"@abp/ng.permission-management": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.components": "~7.3.0-rc.1",
"@abp/ng.permission-management": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

57
npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts

@ -1,4 +1,9 @@
import { ConfigStateService } from '@abp/ng.core';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { ConfigStateService, IAbpGuard } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
@ -6,18 +11,8 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.theme.shared/extensions';
import { Injectable, Injector } from '@angular/core';
import { Observable } from 'rxjs';
import { map, mapTo, tap } from 'rxjs/operators';
import { eIdentityComponents } from '../enums/components';
import {
IdentityCreateFormPropContributors,
IdentityEditFormPropContributors,
IdentityEntityActionContributors,
IdentityEntityPropContributors,
IdentityToolbarActionContributors,
} from '../models/config-options';
import {
DEFAULT_IDENTITY_CREATE_FORM_PROPS,
DEFAULT_IDENTITY_EDIT_FORM_PROPS,
@ -32,60 +27,56 @@ import {
} from '../tokens/extensions.token';
@Injectable()
export class IdentityExtensionsGuard {
constructor(private injector: Injector) {}
export class IdentityExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
const extensions: ExtensionsService = this.injector.get(ExtensionsService);
const actionContributors: IdentityEntityActionContributors =
this.injector.get(IDENTITY_ENTITY_ACTION_CONTRIBUTORS, null) || {};
const toolbarContributors: IdentityToolbarActionContributors =
this.injector.get(IDENTITY_TOOLBAR_ACTION_CONTRIBUTORS, null) || {};
const propContributors: IdentityEntityPropContributors =
this.injector.get(IDENTITY_ENTITY_PROP_CONTRIBUTORS, null) || {};
const createFormContributors: IdentityCreateFormPropContributors =
this.injector.get(IDENTITY_CREATE_FORM_PROP_CONTRIBUTORS, null) || {};
const editFormContributors: IdentityEditFormPropContributors =
this.injector.get(IDENTITY_EDIT_FORM_PROP_CONTRIBUTORS, null) || {};
const config = { optional: true };
const actionContributors = inject(IDENTITY_ENTITY_ACTION_CONTRIBUTORS, config) || {};
const toolbarContributors = inject(IDENTITY_TOOLBAR_ACTION_CONTRIBUTORS, config) || {};
const propContributors = inject(IDENTITY_ENTITY_PROP_CONTRIBUTORS, config) || {};
const createFormContributors = inject(IDENTITY_CREATE_FORM_PROP_CONTRIBUTORS, config) || {};
const editFormContributors = inject(IDENTITY_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
const configState = this.injector.get(ConfigStateService);
return getObjectExtensionEntitiesFromStore(configState, 'Identity').pipe(
return getObjectExtensionEntitiesFromStore(this.configState, 'Identity').pipe(
map(entities => ({
[eIdentityComponents.Roles]: entities.Role,
[eIdentityComponents.Users]: entities.User,
})),
mapEntitiesToContributors(configState, 'AbpIdentity'),
mapEntitiesToContributors(this.configState, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
extensions.entityActions,
this.extensions.entityActions,
DEFAULT_IDENTITY_ENTITY_ACTIONS,
actionContributors,
);
mergeWithDefaultActions(
extensions.toolbarActions,
this.extensions.toolbarActions,
DEFAULT_IDENTITY_TOOLBAR_ACTIONS,
toolbarContributors,
);
mergeWithDefaultProps(
extensions.entityProps,
this.extensions.entityProps,
DEFAULT_IDENTITY_ENTITY_PROPS,
objectExtensionContributors.prop,
propContributors,
);
mergeWithDefaultProps(
extensions.createFormProps,
this.extensions.createFormProps,
DEFAULT_IDENTITY_CREATE_FORM_PROPS,
objectExtensionContributors.createForm,
createFormContributors,
);
mergeWithDefaultProps(
extensions.editFormProps,
this.extensions.editFormProps,
DEFAULT_IDENTITY_EDIT_FORM_PROPS,
objectExtensionContributors.editForm,
editFormContributors,
);
}),
mapTo(true),
map(() => true),
);
}
}

16
npm/ng-packs/packages/identity/src/lib/identity-routing.module.ts

@ -1,21 +1,25 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, mapToCanActivate } from '@angular/router';
import {
AuthGuard, PermissionGuard,
AuthGuard,
PermissionGuard,
ReplaceableComponents,
ReplaceableRouteContainerComponent, RouterOutletComponent
ReplaceableRouteContainerComponent,
RouterOutletComponent,
} from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RolesComponent } from './components/roles/roles.component';
import { UsersComponent } from './components/users/users.component';
import { eIdentityComponents } from './enums/components';
import { IdentityExtensionsGuard } from './guards/extensions.guard';
import { IdentityExtensionsGuard } from './guards';
const routes: Routes = [
{ path: '', redirectTo: 'roles', pathMatch: 'full' },
{
path: '',
component: RouterOutletComponent,
canActivate: [AuthGuard, PermissionGuard, IdentityExtensionsGuard],
canActivate: mapToCanActivate([AuthGuard, PermissionGuard, IdentityExtensionsGuard]),
children: [
{
path: 'roles',

8
npm/ng-packs/packages/oauth/package.json

@ -1,17 +1,17 @@
{
"name": "@abp/ng.oauth",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.core": "~7.2.2",
"@abp/utils": "~7.2.2",
"@abp/ng.core": "~7.3.0-rc.1",
"@abp/utils": "~7.3.0-rc.1",
"angular-oauth2-oidc": "^15.0.1",
"just-clone": "^6.1.1",
"just-compare": "^1.4.0",
"just-compare": "^2.3.0",
"tslib": "^2.0.0"
},
"publishConfig": {

30
npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts

@ -1,26 +1,36 @@
import { Injectable } from '@angular/core';
import { CanActivate, UrlTree, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Injectable, inject } from '@angular/core';
import { UrlTree, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable, delay, of, tap } from 'rxjs';
import { OAuthService } from 'angular-oauth2-oidc';
import { Observable } from 'rxjs';
import { AuthService, IAuthGuard } from '@abp/ng.core';
import { AuthService, HttpErrorReporterService, IAbpGuard } from '@abp/ng.core';
@Injectable({
providedIn: 'root',
})
export class AbpOAuthGuard implements IAuthGuard {
constructor(private oauthService: OAuthService, private authService: AuthService) {}
export class AbpOAuthGuard implements IAbpGuard {
protected readonly oAuthService = inject(OAuthService);
protected readonly authService = inject(AuthService);
protected readonly httpErrorReporter = inject(HttpErrorReporterService);
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean> | boolean | UrlTree {
const hasValidAccessToken = this.oauthService.hasValidAccessToken();
const hasValidAccessToken = this.oAuthService.hasValidAccessToken();
if (hasValidAccessToken) {
return true;
}
const params = { returnUrl: state.url };
this.authService.navigateToLogin(params);
return false;
return of(false).pipe(
tap(() => this.httpErrorReporter.reportError({ status: 401 } as HttpErrorResponse)),
delay(1500),
tap(() => {
const params = { returnUrl: state.url };
this.authService.navigateToLogin(params);
}),
);
}
}

4
npm/ng-packs/packages/permission-management/package.json

@ -1,13 +1,13 @@
{
"name": "@abp/ng.permission-management",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

2
npm/ng-packs/packages/schematics/package.json

@ -1,6 +1,6 @@
{
"name": "@abp/ng.schematics",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"description": "Schematics that works with ABP Backend",
"keywords": [
"schematics"

6
npm/ng-packs/packages/setting-management/package.json

@ -1,14 +1,14 @@
{
"name": "@abp/ng.setting-management",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.components": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.components": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

6
npm/ng-packs/packages/setting-management/src/lib/setting-management-routing.module.ts

@ -1,11 +1,11 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, mapToCanActivate } from '@angular/router';
import {
AuthGuard,
ReplaceableComponents,
ReplaceableRouteContainerComponent,
RouterOutletComponent,
} from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SettingManagementComponent } from './components/setting-management.component';
import { eSettingManagementComponents } from './enums/components';
@ -13,7 +13,7 @@ const routes: Routes = [
{
path: '',
component: RouterOutletComponent,
canActivate: [AuthGuard],
canActivate: mapToCanActivate([AuthGuard]),
children: [
{
path: '',

6
npm/ng-packs/packages/tenant-management/package.json

@ -1,14 +1,14 @@
{
"name": "@abp/ng.tenant-management",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.feature-management": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.feature-management": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

59
npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts

@ -1,4 +1,9 @@
import { ConfigStateService } from '@abp/ng.core';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { ConfigStateService, IAbpGuard } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
@ -6,18 +11,8 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.theme.shared/extensions';
import { Injectable, Injector } from '@angular/core';
import { Observable } from 'rxjs';
import { map, mapTo, tap } from 'rxjs/operators';
import { eTenantManagementComponents } from '../enums/components';
import {
TenantManagementCreateFormPropContributors,
TenantManagementEditFormPropContributors,
TenantManagementEntityActionContributors,
TenantManagementEntityPropContributors,
TenantManagementToolbarActionContributors,
} from '../models/config-options';
import {
DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS,
DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS,
@ -32,59 +27,57 @@ import {
} from '../tokens/extensions.token';
@Injectable()
export class TenantManagementExtensionsGuard {
constructor(private injector: Injector) {}
export class TenantManagementExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
const extensions: ExtensionsService = this.injector.get(ExtensionsService);
const actionContributors: TenantManagementEntityActionContributors =
this.injector.get(TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, null) || {};
const toolbarContributors: TenantManagementToolbarActionContributors =
this.injector.get(TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, null) || {};
const propContributors: TenantManagementEntityPropContributors =
this.injector.get(TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, null) || {};
const createFormContributors: TenantManagementCreateFormPropContributors =
this.injector.get(TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, null) || {};
const editFormContributors: TenantManagementEditFormPropContributors =
this.injector.get(TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, null) || {};
const config = { optional: true };
const actionContributors = inject(TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, config) || {};
const toolbarContributors = inject(TENANT_MANAGEMENT_TOOLBAR_ACTION_CONTRIBUTORS, config) || {};
const propContributors = inject(TENANT_MANAGEMENT_ENTITY_PROP_CONTRIBUTORS, config) || {};
const createFormContributors =
inject(TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, config) || {};
const editFormContributors =
inject(TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
const configState = this.injector.get(ConfigStateService);
return getObjectExtensionEntitiesFromStore(configState, 'TenantManagement').pipe(
return getObjectExtensionEntitiesFromStore(this.configState, 'TenantManagement').pipe(
map(entities => ({
[eTenantManagementComponents.Tenants]: entities.Tenant,
})),
mapEntitiesToContributors(configState, 'TenantManagement'),
mapEntitiesToContributors(this.configState, 'TenantManagement'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
extensions.entityActions,
this.extensions.entityActions,
DEFAULT_TENANT_MANAGEMENT_ENTITY_ACTIONS,
actionContributors,
);
mergeWithDefaultActions(
extensions.toolbarActions,
this.extensions.toolbarActions,
DEFAULT_TENANT_MANAGEMENT_TOOLBAR_ACTIONS,
toolbarContributors,
);
mergeWithDefaultProps(
extensions.entityProps,
this.extensions.entityProps,
DEFAULT_TENANT_MANAGEMENT_ENTITY_PROPS,
objectExtensionContributors.prop,
propContributors,
);
mergeWithDefaultProps(
extensions.createFormProps,
this.extensions.createFormProps,
DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS,
objectExtensionContributors.createForm,
createFormContributors,
);
mergeWithDefaultProps(
extensions.editFormProps,
this.extensions.editFormProps,
DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS,
objectExtensionContributors.editForm,
editFormContributors,
);
}),
mapTo(true),
map(() => true),
);
}
}

8
npm/ng-packs/packages/tenant-management/src/lib/tenant-management-routing.module.ts

@ -1,3 +1,6 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, mapToCanActivate } from '@angular/router';
import {
AuthGuard,
PermissionGuard,
@ -5,8 +8,7 @@ import {
ReplaceableRouteContainerComponent,
RouterOutletComponent,
} from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TenantsComponent } from './components/tenants/tenants.component';
import { eTenantManagementComponents } from './enums/components';
import { TenantManagementExtensionsGuard } from './guards';
@ -16,7 +18,7 @@ const routes: Routes = [
{
path: '',
component: RouterOutletComponent,
canActivate: [AuthGuard, PermissionGuard, TenantManagementExtensionsGuard],
canActivate: mapToCanActivate([AuthGuard, PermissionGuard, TenantManagementExtensionsGuard]),
children: [
{
path: 'tenants',

6
npm/ng-packs/packages/theme-basic/package.json

@ -1,14 +1,14 @@
{
"name": "@abp/ng.theme.basic",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.account.core": "~7.2.2",
"@abp/ng.theme.shared": "~7.2.2",
"@abp/ng.account.core": "~7.3.0-rc.1",
"@abp/ng.theme.shared": "~7.3.0-rc.1",
"tslib": "^2.0.0"
},
"publishConfig": {

4
npm/ng-packs/packages/theme-shared/package.json

@ -1,13 +1,13 @@
{
"name": "@abp/ng.theme.shared",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.core": "~7.2.2",
"@abp/ng.core": "~7.3.0-rc.1",
"@fortawesome/fontawesome-free": "^5.15.4",
"@ng-bootstrap/ng-bootstrap": "^15.0.0",
"@ngx-validate/core": "^0.2.0",

4
npm/packs/anchor-js/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/anchor-js",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"anchor-js": "^4.3.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/aspnetcore.components.server.basictheme/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/aspnetcore.components.server.basictheme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/aspnetcore.components.server.theming": "~7.2.2"
"@abp/aspnetcore.components.server.theming": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

6
npm/packs/aspnetcore.components.server.theming/package.json

@ -1,12 +1,12 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/aspnetcore.components.server.theming",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/bootstrap": "~7.2.2",
"@abp/font-awesome": "~7.2.2"
"@abp/bootstrap": "~7.3.0-rc.1",
"@abp/font-awesome": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

4
npm/packs/aspnetcore.mvc.ui.theme.basic/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/aspnetcore.mvc.ui.theme.basic",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.shared": "~7.2.2"
"@abp/aspnetcore.mvc.ui.theme.shared": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

34
npm/packs/aspnetcore.mvc.ui.theme.shared/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/aspnetcore.mvc.ui.theme.shared",
"repository": {
"type": "git",
@ -10,22 +10,22 @@
"access": "public"
},
"dependencies": {
"@abp/aspnetcore.mvc.ui": "~7.2.2",
"@abp/bootstrap": "~7.2.2",
"@abp/bootstrap-datepicker": "~7.2.2",
"@abp/bootstrap-daterangepicker": "~7.2.2",
"@abp/datatables.net-bs5": "~7.2.2",
"@abp/font-awesome": "~7.2.2",
"@abp/jquery-form": "~7.2.2",
"@abp/jquery-validation-unobtrusive": "~7.2.2",
"@abp/lodash": "~7.2.2",
"@abp/luxon": "~7.2.2",
"@abp/malihu-custom-scrollbar-plugin": "~7.2.2",
"@abp/moment": "~7.2.2",
"@abp/select2": "~7.2.2",
"@abp/sweetalert2": "~7.2.2",
"@abp/timeago": "~7.2.2",
"@abp/toastr": "~7.2.2"
"@abp/aspnetcore.mvc.ui": "~7.3.0-rc.1",
"@abp/bootstrap": "~7.3.0-rc.1",
"@abp/bootstrap-datepicker": "~7.3.0-rc.1",
"@abp/bootstrap-daterangepicker": "~7.3.0-rc.1",
"@abp/datatables.net-bs5": "~7.3.0-rc.1",
"@abp/font-awesome": "~7.3.0-rc.1",
"@abp/jquery-form": "~7.3.0-rc.1",
"@abp/jquery-validation-unobtrusive": "~7.3.0-rc.1",
"@abp/lodash": "~7.3.0-rc.1",
"@abp/luxon": "~7.3.0-rc.1",
"@abp/malihu-custom-scrollbar-plugin": "~7.3.0-rc.1",
"@abp/moment": "~7.3.0-rc.1",
"@abp/select2": "~7.3.0-rc.1",
"@abp/sweetalert2": "~7.3.0-rc.1",
"@abp/timeago": "~7.3.0-rc.1",
"@abp/toastr": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

2
npm/packs/aspnetcore.mvc.ui/package-lock.json

@ -1,6 +1,6 @@
{
"name": "@abp/aspnetcore.mvc.ui",
"version": "7.2.2",
"version": "7.3.0-rc.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

2
npm/packs/aspnetcore.mvc.ui/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/aspnetcore.mvc.ui",
"repository": {
"type": "git",

10
npm/packs/blogging/package.json

@ -1,14 +1,14 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/blogging",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.shared": "~7.2.2",
"@abp/owl.carousel": "~7.2.2",
"@abp/prismjs": "~7.2.2",
"@abp/tui-editor": "~7.2.2"
"@abp/aspnetcore.mvc.ui.theme.shared": "~7.3.0-rc.1",
"@abp/owl.carousel": "~7.3.0-rc.1",
"@abp/prismjs": "~7.3.0-rc.1",
"@abp/tui-editor": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

2
npm/packs/bootstrap-datepicker/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/bootstrap-datepicker",
"repository": {
"type": "git",

2
npm/packs/bootstrap-daterangepicker/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/bootstrap-daterangepicker",
"repository": {
"type": "git",

4
npm/packs/bootstrap/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/bootstrap",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"bootstrap": "^5.1.3"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

2
npm/packs/chart.js/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/chart.js",
"publishConfig": {
"access": "public"

4
npm/packs/clipboard/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/clipboard",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"clipboard": "^2.0.8"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

12
npm/packs/cms-kit.admin/package.json

@ -1,15 +1,15 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/cms-kit.admin",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/codemirror": "~7.2.2",
"@abp/jstree": "~7.2.2",
"@abp/slugify": "~7.2.2",
"@abp/tui-editor": "~7.2.2",
"@abp/uppy": "~7.2.2"
"@abp/codemirror": "~7.3.0-rc.1",
"@abp/jstree": "~7.3.0-rc.1",
"@abp/slugify": "~7.3.0-rc.1",
"@abp/tui-editor": "~7.3.0-rc.1",
"@abp/uppy": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

6
npm/packs/cms-kit.public/package.json

@ -1,12 +1,12 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/cms-kit.public",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/highlight.js": "~7.2.2",
"@abp/star-rating-svg": "~7.2.2"
"@abp/highlight.js": "~7.3.0-rc.1",
"@abp/star-rating-svg": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

6
npm/packs/cms-kit/package.json

@ -1,12 +1,12 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/cms-kit",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/cms-kit.admin": "~7.2.2",
"@abp/cms-kit.public": "~7.2.2"
"@abp/cms-kit.admin": "~7.3.0-rc.1",
"@abp/cms-kit.public": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

4
npm/packs/codemirror/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/codemirror",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"codemirror": "^5.65.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/core/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/core",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/utils": "~7.2.2"
"@abp/utils": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

4
npm/packs/cropperjs/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/cropperjs",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"cropperjs": "^1.5.12"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/datatables.net-bs4/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/datatables.net-bs4",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/datatables.net": "~7.2.2",
"@abp/datatables.net": "~7.3.0-rc.1",
"datatables.net-bs4": "^1.11.4"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/datatables.net-bs5/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/datatables.net-bs5",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/datatables.net": "~7.2.2",
"@abp/datatables.net": "~7.3.0-rc.1",
"datatables.net-bs5": "^1.11.4"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/datatables.net/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/datatables.net",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/jquery": "~7.2.2",
"@abp/jquery": "~7.3.0-rc.1",
"datatables.net": "^1.11.4"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

12
npm/packs/docs/package.json

@ -1,15 +1,15 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/docs",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/anchor-js": "~7.2.2",
"@abp/clipboard": "~7.2.2",
"@abp/malihu-custom-scrollbar-plugin": "~7.2.2",
"@abp/popper.js": "~7.2.2",
"@abp/prismjs": "~7.2.2"
"@abp/anchor-js": "~7.3.0-rc.1",
"@abp/clipboard": "~7.3.0-rc.1",
"@abp/malihu-custom-scrollbar-plugin": "~7.3.0-rc.1",
"@abp/popper.js": "~7.3.0-rc.1",
"@abp/prismjs": "~7.3.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"
}

2
npm/packs/flag-icon-css/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/flag-icon-css",
"publishConfig": {
"access": "public"

2
npm/packs/flag-icons/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/flag-icons",
"publishConfig": {
"access": "public"

4
npm/packs/font-awesome/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/font-awesome",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"@fortawesome/fontawesome-free": "^5.15.4"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/highlight.js/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/highlight.js",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"@highlightjs/cdn-assets": "~11.4.0"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/jquery-form/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/jquery-form",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/jquery": "~7.2.2",
"@abp/jquery": "~7.3.0-rc.1",
"jquery-form": "^4.3.0"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/jquery-validation-unobtrusive/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/jquery-validation-unobtrusive",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/jquery-validation": "~7.2.2",
"@abp/jquery-validation": "~7.3.0-rc.1",
"jquery-validation-unobtrusive": "^3.2.12"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/jquery-validation/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/jquery-validation",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/jquery": "~7.2.2",
"@abp/jquery": "~7.3.0-rc.1",
"jquery-validation": "^1.19.3"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/jquery/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/jquery",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"jquery": "~3.6.0"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/jstree/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/jstree",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/jquery": "~7.2.2",
"@abp/jquery": "~7.3.0-rc.1",
"jstree": "^3.3.12"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/lodash/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/lodash",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"lodash": "^4.17.21"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/luxon/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/luxon",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"luxon": "^2.3.0"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/malihu-custom-scrollbar-plugin/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/malihu-custom-scrollbar-plugin",
"repository": {
"type": "git",
@ -10,7 +10,7 @@
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"malihu-custom-scrollbar-plugin": "^3.1.5"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/markdown-it/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/markdown-it",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"markdown-it": "^12.3.2"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

2
npm/packs/moment/package.json

@ -1,5 +1,5 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/moment",
"repository": {
"type": "git",

4
npm/packs/owl.carousel/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/owl.carousel",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"owl.carousel": "^2.3.4"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

4
npm/packs/popper.js/package.json

@ -1,11 +1,11 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/popper.js",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/core": "~7.2.2",
"@abp/core": "~7.3.0-rc.1",
"@popperjs/core": "^2.11.2"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

6
npm/packs/prismjs/package.json

@ -1,12 +1,12 @@
{
"version": "7.2.2",
"version": "7.3.0-rc.1",
"name": "@abp/prismjs",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@abp/clipboard": "~7.2.2",
"@abp/core": "~7.2.2",
"@abp/clipboard": "~7.3.0-rc.1",
"@abp/core": "~7.3.0-rc.1",
"prismjs": "^1.26.0"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431"

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

Loading…
Cancel
Save