Browse Source

Merge branch 'dev' into maliming/UserSecurityLog

pull/4675/head
maliming 6 years ago
parent
commit
f31c7ba36b
  1. 35
      README.md
  2. 1
      build/common.ps1
  3. 4
      common.props
  4. 177
      docs/en/Blog-Posts/2020-07-01 v3_0_Release/Post.md
  5. BIN
      docs/en/Blog-Posts/2020-07-01 v3_0_Release/file-management-ui.png
  6. 72
      docs/en/Getting-Started-React-Native.md
  7. 146
      docs/en/Getting-Started.md
  8. 2
      docs/en/How-To/Azure-Active-Directory-Authentication-MVC.md
  9. 2
      docs/en/How-To/Customize-Login-Page-MVC.md
  10. 2
      docs/en/How-To/Customize-SignIn-Manager.md
  11. 6
      docs/en/Index.md
  12. 2
      docs/en/Repositories.md
  13. 1
      docs/en/Road-Map.md
  14. 6
      docs/en/Samples/Index.md
  15. 2
      docs/en/Samples/Microservice-Demo.md
  16. 911
      docs/en/Tutorials/Part-1.md
  17. 1307
      docs/en/Tutorials/Part-2.md
  18. 1227
      docs/en/Tutorials/Part-3.md
  19. 248
      docs/en/Tutorials/Part-4.md
  20. 401
      docs/en/Tutorials/Part-5.md
  21. BIN
      docs/en/Tutorials/images/bookstore-book-and-booktype.png
  22. BIN
      docs/en/Tutorials/images/bookstore-book-list-3.png
  23. BIN
      docs/en/Tutorials/images/bookstore-book-list.png
  24. BIN
      docs/en/Tutorials/images/bookstore-dbmigrator-on-solution.png
  25. BIN
      docs/en/Tutorials/images/bookstore-edit-button-2.png
  26. BIN
      docs/en/Tutorials/images/bookstore-edit-delete-actions.png
  27. BIN
      docs/en/Tutorials/images/bookstore-getlist-result-network.png
  28. BIN
      docs/en/Tutorials/images/bookstore-index-js-file-v3.png
  29. BIN
      docs/en/Tutorials/images/bookstore-javascript-proxy-console.png
  30. BIN
      docs/en/Tutorials/images/bookstore-new-book-button-2.png
  31. BIN
      docs/en/Tutorials/images/bookstore-new-book-button-small.png
  32. BIN
      docs/en/Tutorials/images/bookstore-permissions-ui.png
  33. BIN
      docs/en/Tutorials/images/generated-proxies-2.png
  34. 71
      docs/en/UI/Angular/Config-State.md
  35. 51
      docs/en/UI/Angular/Custom-Setting-Page.md
  36. 21
      docs/en/UI/Angular/Localization.md
  37. 175
      docs/en/UI/Angular/Migration-Guide-v3.md
  38. 73
      docs/en/UI/Angular/Modifying-the-Menu.md
  39. 8
      docs/en/UI/Angular/Permission-Management.md
  40. 6
      docs/en/UI/Angular/Service-Proxies.md
  41. BIN
      docs/en/UI/Angular/images/custom-settings.png
  42. BIN
      docs/en/UI/Angular/images/generated-files-via-generate-proxy.png
  43. BIN
      docs/en/UI/Angular/images/navigation-menu-search-input.png
  44. 25
      docs/en/docs-nav.json
  45. 177
      docs/zh-Hans/Blog-Posts/2020-07-01 v3_0_Release/Post.md
  46. BIN
      docs/zh-Hans/Blog-Posts/2020-07-01 v3_0_Release/file-management-ui.png
  47. 2
      docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md
  48. 2
      docs/zh-Hans/How-To/Customize-Login-Page-MVC.md
  49. 2
      docs/zh-Hans/How-To/Customize-SignIn-Manager.md
  50. 4
      docs/zh-Hans/Repositories.md
  51. 2
      docs/zh-Hans/Samples/Index.md
  52. 5
      docs/zh-Hans/Tutorials/Part-1.md
  53. 2
      docs/zh-Hans/docs-nav.json
  54. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  55. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  56. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  57. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHooks.cs
  58. 34
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs
  59. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs
  60. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  61. 5
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  62. 6
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
  63. 38
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  64. 7
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider.cs
  65. 76
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs
  66. 10
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs
  67. 7
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs
  68. 7
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.csproj
  69. 7
      modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj
  70. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  71. 5
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  72. 2
      modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs
  73. 12
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml
  74. 7
      modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj
  75. 17
      modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo.Blogging.Admin.Application.Contracts.csproj
  76. 1
      modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/BloggingAdminApplicationContractsModule.cs
  77. 2
      modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminWebModule.cs
  78. 5
      modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj
  79. 6
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo.Blogging.Application.Contracts.csproj
  80. 7
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo.Blogging.Domain.Shared.csproj
  81. 2
      modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs
  82. 5
      modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj
  83. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo.Docs.Admin.Application.Contracts.csproj
  84. 2
      modules/docs/src/Volo.Docs.Admin.Web/DocsAdminWebModule.cs
  85. 5
      modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj
  86. 2
      modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
  87. 1
      modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs
  88. 2
      modules/docs/src/Volo.Docs.Web/DocsWebModule.cs
  89. 2
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs
  90. 2
      modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj
  91. 7
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj
  92. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs
  93. 5
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj
  94. 5
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo.Abp.Identity.Domain.Shared.csproj
  95. 2
      modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs
  96. 5
      modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj
  97. 5
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo.Abp.PermissionManagement.Domain.Shared.csproj
  98. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs
  99. 5
      modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj
  100. 5
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo.Abp.SettingManagement.Domain.Shared.csproj

35
README.md

@ -1,34 +1,23 @@
# ABP
# ABP Framework
![build and test](https://github.com/abpframework/abp/workflows/build%20and%20test/badge.svg)
[![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
[![MyGet (with prereleases)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds)
[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core)
This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework. See [the announcement](https://blog.abp.io/abp/Abp-vNext-Announcement).
ABP is an **open source application framework** focused on ASP.NET Core based web application development, but also supports developing other type of applications.
See the official [web site (abp.io)](https://abp.io/) for more information.
## Links
### Documentation
* <a href="https://abp.io/" target="_blank">Official Web Site</a>
* <a href="https://abp.io/get-started" target="_blank">Get Started</a>
* <a href="https://abp.io/features" target="_blank">Features</a>
* <a href="https://docs.abp.io/" target="_blank">Documentation</a>
* <a href="https://docs.abp.io/en/abp/latest/Samples/Index" target="_blank">Samples</a>
* <a href="https://blog.abp.io/" target="_blank">Blog</a>
* <a href="https://stackoverflow.com/questions/tagged/abp" target="_blank">Stack overflow</a>
* <a href="https://twitter.com/abpframework" target="_blank">Twitter</a>
See the <a href="https://docs.abp.io/" target="_blank">documentation</a>.
### Development
#### Pre Requirements
- Visual Studio 2019 16.4.0+
#### Framework
Framework solution is located under the `framework` folder. It has no external dependency.
#### Modules/Templates
[Modules](modules/) and [Templates](templates/) have their own solutions and have **local references** to the framework and each other.
Visual Studio can not work properly with the local references out of the solution folder. When you open a module/sample solution in the Visual Studio, you may get some errors related to the dependencies. In this case, run the `dotnet restore` on the command prompt for the related solution's folder. You need to run it after you first open the solution or change a dependency.
### Contribution
## Contribution
ABP is an open source platform. Check [the contribution guide](docs/en/Contribution/Index.md) if you want to contribute to the project.

1
build/common.ps1

@ -22,6 +22,5 @@ $solutionPaths = (
"../modules/virtual-file-explorer",
"../templates/module/aspnet-core",
"../templates/app/aspnet-core",
"../samples/MicroserviceDemo",
"../abp_io/AbpIoLocalization"
)

4
common.props

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

177
docs/en/Blog-Posts/2020-07-01 v3_0_Release/Post.md

@ -0,0 +1,177 @@
# ABP Framework v3.0 Has Been Released
We are excited to announce that the **ABP Framework** & and the **ABP Commercial** version 3.0 have been released. As different than the regular release lifecycle, which is 2-weeks, this version has taken 4-weeks with **119 [issues](https://github.com/abpframework/abp/issues?q=is%3Aopen+is%3Aissue+milestone%3A3.0)** closed, **89 [pull requests](https://github.com/abpframework/abp/pulls?q=is%3Aopen+is%3Apr+milestone%3A3.0)** merged and **798 commits** done in the main framework [repository](https://github.com/abpframework/abp).
Since this is a **major version**, it also includes some **breaking changes**. Don't panic, the changes are easy to adapt and will be explained below.
> See the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/3.0.0) for a detailed change log.
## What's New with the ABP Framework 3.0?
This post will only cover the important features/changes. You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.9.0).
### Angular 10!
Angular version 10 has just been [released](https://blog.angular.io/version-10-of-angular-now-available-78960babd41) and we've immediately migrated the [startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Application) to Angular 10! So, when you [create a new solution](https://abp.io/get-started) with the Angular UI, you will take the advantage of the new Angular.
We've prepared a [migration guide](https://github.com/abpframework/abp/blob/dev/docs/en/UI/Angular/Migration-Guide-v3.md) for the projects created an older version and want to migrate to Angular 10.
### The Oracle Integration Package
We had created [an integration package](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) for the Oracle for EF Core based applications using the Devart's library since the official Oracle EF Core package was not supporting the EF Core 3.1. It now supports as a [beta release](https://www.nuget.org/packages/Oracle.EntityFrameworkCore/3.19.0-beta1). While it is in beta, we've created [the integration package](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle), so you can use it in your application.
See [the documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Oracle) for details.
### Azure BLOB Storage Provider
We had created a [BLOB storing system](https://docs.abp.io/en/abp/latest/Blob-Storing) in the previous version with a file system and database storage provider. This release introduces the Azure BLOB Storage provider. See [the documentation](https://docs.abp.io/en/abp/latest/Blob-Storing-Azure).
### Distributed Cache Bulk Operations & the New Redis Cache Package
The [standard IDistributeCache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) interface of the ASP.NET Core doesn't contain **bulk operations**, like setting multiple items with a single method/server call. ABP Framework introduces new methods those can be used for bulk operations on the ABP's `IDistributedCache<T>` interface:
* GetManyAsync / GetMany
* SetManyAsync / SetMany
Then we needed to implement these new methods for Redis cache and [had to create](https://github.com/abpframework/abp/issues/4483) a Redis integration package which extends the Microsoft's implementation.
These methods are also used by the ABP Framework to cache settings, features and permissions for a user/role/tenant and brings a **significant performance improvement**.
See the [caching document](https://docs.abp.io/en/abp/latest/Caching) for details.
### Embedded Files Manifest Support for the Virtual File System
Virtual File System now supports to use `GenerateEmbeddedFilesManifest` in your projects to add the **real file/directory structure** of your embedded resources in the compiled assembly. So, you can now access to the files without any file name restriction (previously, some special chars like `.` in the directory names was a problem in some cases)
See [the documentation](https://docs.abp.io/en/abp/latest/Virtual-File-System) to learn how to take the advantage of new system.
### New Samples
Based on the requests from the community, we've prepared two new sample applications:
* [StoredProcedureDemo](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo) demonstrates how to call stored procedures, views and functions inside a custom repository.
* [OrganizationUnitSample](https://github.com/abpframework/abp-samples/tree/master/OrganizationUnitSample) shows how to use the organization unit system of the [Identity module](https://docs.abp.io/en/abp/latest/Modules/Identity) for your entities.
### DynamicStringLength & DynamicMaxLength Attributes
The standard `StringLength` and `MaxLength` data annotation attributes is useful to validate properties of a class when the class is used as a Model or [DTO](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects). However, just like any other attribute, the length values should be literal (constant) values known at **compile time**.
**Example: Using the `StringLength`**
````csharp
public class CreateBookDto
{
public const int MaxNameLength = 128; //CONSTANT!
[StringLength(MaxNameLength)]
public string Name { get; set; }
}
````
ABP Framework now has the `DynamicStringLength` & `DynamicMaxLength` properties to allow to determine the lengths at **runtime**.
**Example: Using the `DynamicStringLength`**
````csharp
public class CreateBookDto
{
public static int MaxNameLength { get; set; } = 128;
[DynamicStringLength(typeof(CreateBookDto), nameof(MaxNameLength))]
public string Name { get; set; }
}
````
`DynamicStringLength` gets a class **type** and the **name** of a static property on this class to read the max length (there is also a minimum length option just like the `StringLength`).
This allows you to get the max value from a configuration and set on the application startup (generally, in the `PreConfigureServices` method of your [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics)):
````csharp
CreateBookDto.MaxNameLength = 200;
````
This feature is used by the [pre-built application modules](https://docs.abp.io/en/abp/latest/Modules/Index), so you can now override the max lengths of the properties defined in these modules.
### Auto Distributed Events
ABP can **automatically publish distributed events** for all entities on their create, update and delete events. That's pretty useful since you commonly interest in these basic events in a distributed system.
This feature is **mature and [documented](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus#pre-defined-events)** with the v3.0. You can easily configure some or all the entities to be published.
### IAsyncQueryableExecuter
When you work with LINQ extension methods, you need to call `ToListAsync()`, `FirstOrDefaultAsync()`... methods on your queries. Unfortunately, these methods are **not standard** LINQ extension methods. They are defined in the [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) package (or in the [MongoDB.Driver](https://www.nuget.org/packages/MongoDB.Driver/) package if you are using the MongoDB).
So, you need to depend on this package if you want to use the async extension methods. That breaks the layering and makes your application or domain layer depends on the EF Core / MongoDB package.
`IAsyncQueryableExecuter` is a service defined by the ABP Framework to **execute queries asynchronously without depending the specific provider** (EF Core / MongoDB) package.
See [the documentation](https://docs.abp.io/en/abp/latest/Repositories#iqueryable-async-operations) to read the details and learn our recommendations.
### API documentation
We are now publishing [API documents](https://docs.abp.io/api-docs/abp/2.9/api/index.html) for the ABP Framework and modules in every release. So, you can explore the ABP Framework classes much more easier than before. Click the the **API Documentation** link on the navigation menu of the [documentation](https://docs.abp.io/en/abp/latest/).
### Package List
We have [created a page](http://abp.io/packages) to list all the ABP-related official NuGet and NPM packages.
### Others
* Implemented front-channel server-side clients [sign out](https://identityserver4.readthedocs.io/en/latest/topics/signout.html) for the identity server.
* `abp.currentUser` (`CurrentUser` service in the Angular UI) now has a `roles` array that contains role names of the current user.
* Upgraded all the NuGet and NPM package dependencies.
* Introduced `ReadOnlyAppService` base class (which has only the get operations) in addition to the `CrudAppService` base class (which has get, create, update and delete operations).
See the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/3.0.0) for others updates.
## What's New with the ABP Commercial 3.0?
In addition to all the features coming with the ABP Framework, the ABP Commercial has additional features with this release, as always. This section covers the [ABP Commercial](https://commercial.abp.io/) highlights in the version 2.9.
### New File Management Module
We've created a new module that is used to store and manage files in your application. This new module is based on the [BLOB Storing system](https://docs.abp.io/en/abp/latest/Blob-Storing), so it can use different storage providers to store the file contents.
**Example screenshot**
![file-management-ui](file-management-ui.png)
You can upload, download and organize files in a hierarchical folder structure. It is also compatible to multi-tenancy and you can determine total size limit for your tenants. In the next versions, we will be working on a "share" system to share files between users in a more controlled way or share your files with your customers with a public link.
> File Management module is currently available only for the MVC / Razor Pages UI. We are working on the Angular UI and it will be released in the next versions.
## Breaking Changes
Since this is a major version, we've redesigned some APIs and introduced a few "easy to fix" breaking changes.
### ABP Framework
* Changed some **consts** in the pre-built application modules to static properties that is possible to change by your code. If you've used these consts on an attribute, then use the `DynamicStringLength` as explained above.
* Changed `ConcurrencyStamp` max length to 40. You need to **add a database migration** and update your database after upgrading the ABP Framework.
* Using `~` instead of `^` for NPM package dependencies anymore, to be more stable.
### ABP Commercial
* Changed file names for the application logos. Previously, it was using separate logo files for each theme, like `theme1.png`, `theme1-reverse.png`, `theme2.png`, `theme2-reverse.png` (... `6`). Now, we have only two logo files: `logo-light.png` and `logo-dark.png`. So, rename your logo in the `wwwroot/images/logo/` folder for the MVC UI and `/src/assets/images/logo/` folder for the Angular UI.
* We've added the [API documentation](https://docs.abp.io/api-docs/commercial/2.9/api/index.html) for the ABP Commercial too.
> **Also, see the [migration guide](https://github.com/abpframework/abp/blob/dev/docs/en/UI/Angular/Migration-Guide-v3.md) for Angular UI**.
## Known Issues
* 3.0.0 version has a problem with tiered architecture. See [this issue](https://github.com/abpframework/abp/pull/4564) to fix it for your application until we release the v3.0.1.
## About the Next Versions
We will continue to release a new minor/feature version in every two weeks. So, the next expected release date is **2020-07-16** for the version **3.1**.
In the next few versions, we will be focused on the **Blazor UI**, as promised on [the road map](https://docs.abp.io/en/abp/latest/Road-Map). We will continue to improve the documentation, create samples, add other new features and enhancements. Follow the [ABP Framework Twitter account](https://twitter.com/abpframework) for the latest news...
## Bonus: Articles!
Beside developing our products, our team are constantly writing articles/tutorials on various topics. You may want to check the latest articles:
* [What is New in Angular 10?](https://volosoft.com/blog/what-is-new-in-angular-10)
* [Real-Time Messaging In A Distributed Architecture Using ABP, SignalR & RabbitMQ](https://volosoft.com/blog/RealTime-Messaging-Distributed-Architecture-Abp-SingalR-RabbitMQ)
* [How to Use Attribute Directives to Avoid Repetition in Angular Templates](https://volosoft.com/blog/attribute-directives-to-avoid-repetition-in-angular-templates)

BIN
docs/en/Blog-Posts/2020-07-01 v3_0_Release/file-management-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

72
docs/en/Getting-Started-React-Native.md

@ -0,0 +1,72 @@
# Getting Started with the React Native
ABP platform provide basic [React Native](https://reactnative.dev/) startup template to develop mobile applications **integrated to your ABP based backends**.
When you **create a new application** as described in the [getting started document](Getting-Started.md), the solution includes the React Native application in the `react-native` folder as default.
## Configure Your Local IP Address
A React Native application running on an Android emulator or a physical phone **can not connect to the backend** on `localhost`. To fix this problem, it is necessary to run the backend application on your **local IP address**.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `SelfUrl` and `Authority` properties with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* Open the `appsettings.json` in the `.IdentityServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
* Open the `launchSettings.json` in the `.IdentityServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `Authority` property with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ end }}
## Run the Server Application
Run the backend application as described in the [getting started document](Getting-Started.md).
> React Native application does not trust the auto-generated .NET HTTPS certificate. You should use **HTTP** during the development.
Go to the `react-native` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work):
```bash
yarn
```
* Open the `Environment.js` in the `react-native` folder and replace the `localhost` address on the `apiUrl` and `issuer` properties with your local IP address as shown below:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ if Tiered == "Yes" }}
> Make sure that `issuer` matches the running address of the `.IdentityServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
{{else}}
> Make sure that `issuer` and `apiUrl` matches the running address of the `.HttpApi.Host` or `.Web` project.
{{ end }}
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
Wait Expo CLI to start. Expo CLI opens the management interface on the `http://localhost:19002/` address.
![expo-interface](images/rn-expo-interface.png)
In the above management interface, you can start the application with an Android emulator, an iOS simulator or a physical phone by the scan the QR code with the [Expo Client](https://expo.io/tools#client).
> See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/), [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) documents on expo.io.
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
Enter **admin** as the username and **1q2w3E*** as the password to login to the application.
The application is up and running. You can continue to develop your application based on this startup template.

146
docs/en/Getting-Started.md

@ -1,4 +1,4 @@
# Getting started
# Getting Started
````json
//[doc-params]
@ -9,36 +9,36 @@
}
````
This tutorial explains how to create a new {{if UI == "MVC"}} ASP.NET Core MVC web {{else if UI == "NG"}} Angular {{end}} application using the startup template, configure and run it.
This tutorial explains how to create a new {{if UI == "MVC"}} ASP.NET Core MVC (Razor Pages) web {{else if UI == "NG"}} Angular {{end}} application using the startup template.
## Setup your development environment
## Setup Your Development Environment
First things first! Let's setup your development environment before creating the first project.
### Pre-requirements
### Pre-Requirements
The following tools should be installed on your development machine:
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/)
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).*
* [.NET Core 3.1+](https://www.microsoft.com/net/download/dotnet-core/)
* [Node v12+](https://nodejs.org)
* [Yarn v1.19+](https://classic.yarnpkg.com/)
{{ if Tiered == "Yes" }}
* [Redis](https://redis.io/): The applications use Redis as as [distributed cache](../Caching.md). So, you need to have Redis installed & running.
* [Redis](https://redis.io/) (the startup solution uses the Redis as the [distributed cache](Caching.md)).
{{ end }}
> You can use another editor instead of Visual Studio as long as it supports .NET Core and ASP.NET Core.
> *You can use another editor instead of Visual Studio as long as it supports .NET Core and ASP.NET Core.
### Install the ABP CLI
[ABP CLI](./CLI.md) is a command line interface that is used to authenticate and automate some tasks for ABP based applications.
[ABP CLI](./CLI.md) is a command line interface that is used to automate some common tasks for ABP based applications.
> ABP CLI is a free & open source tool for [the ABP framework](https://abp.io/).
> ABP CLI is a free & open source tool for the ABP framework.
First, you need to install the ABP CLI using the following command:
@ -52,27 +52,25 @@ If you've already installed, you can update it using the following command:
dotnet tool update -g Volo.Abp.Cli
````
## Create a new project
## Create a New Project
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
### Using the ABP CLI to create a new project
### Using the ABP CLI to Create a New Project
Use the `new` command of the ABP CLI to create a new project:
````shell
abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
````
* `-t` argument specifies the [startup template](Startup-Templates/Application.md) name. `app` is the startup template that contains the essential [ABP Modules](Modules/Index.md) pre-installed and configured for you.
{{ if UI == "NG" }}
* `-u` argument specifies the UI framework, `angular` in this case.
{{ if Tiered == "Yes" }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint.
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
{{ end }}
@ -92,11 +90,13 @@ abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mon
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
#### ABP CLI commands & options
#### ABP CLI Commands & Options
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. This document uses the [application startup template](Startup-Templates/Application.md) to create a new web application. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
> Alternatively, you can select the "Direct Download" tab from the [ABP Framework web site](https://abp.io/get-started) to create a new solution.
## The solution structure
## The Solution Structure
{{ if UI == "MVC" }}
@ -135,7 +135,7 @@ Open the `.sln` (Visual Studio solution) file under the `aspnet-core` folder:
>
> Your solution may have slightly different structure based on your **UI**, **database** and other preferences.
The solution has a layered structure (based on [Domain Driven Design](./Domain-Driven-Design.md)) and also contains unit & integration test projects.
The solution has a layered structure (based on the [Domain Driven Design](Domain-Driven-Design.md)) and also contains unit & integration test projects.
{{ if DB == "EF" }}
@ -149,9 +149,9 @@ Integration tests projects are properly configured to work with in-memory **Mong
> See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
## Create the database
## Create the Database
### Database connection string
### Connection String
Check the **connection string** in the `appsettings.json` file under the {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}`.Web` project{{end}}{{else if UI == "NG" }}`.HttpApi.Host` project{{end}}:
@ -163,13 +163,13 @@ Check the **connection string** in the `appsettings.json` file under the {{if UI
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) to learn how to switch to another DBMS.
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) to learn how to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
### Apply the migrations
### Apply the Migrations
The solution uses the [Entity Framework Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). So, you need to apply migrations to create the database. There are two ways of applying the database migrations.
#### Apply migrations using the DbMigrator
#### Apply Migrations Using the DbMigrator
The solution comes with a `.DbMigrator` console application which applies migrations and also seed the initial data. It is useful on development as well as on production environment.
@ -183,9 +183,9 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![db-migrator-output](images/db-migrator-output.png)
> Initial seed data creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
#### Using EF Core Update-Database command
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations.
@ -207,7 +207,9 @@ Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations`
This will create a new database based on the configured connection string.
> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
> **Using the `.DbMigrator` tool is the suggested way**, because it also seeds the initial data to be able to properly run the web application.
>
> If you just use the `Update-Database` command, you will have an empty database, so you can not login to the application since there is no initial admin user in the database. You can use the `Update-Database` command in development time when you don't need to seed the database. However, using the `.DbMigrator` application is easier and you can always use it to migrate the schema and seed the database.
{{ else if DB == "Mongo" }}
@ -219,7 +221,7 @@ This will create a new database based on the configured connection string.
The solution is configured to use **MongoDB** in your local computer, so you need to have a MongoDB server instance up and running or change the connection string to another MongoDB server.
### Seed initial data
### Seed Initial Data
The solution comes with a `.DbMigrator` console application which seeds the initial data. It is useful on development as well as on production environment.
@ -233,11 +235,11 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
![db-migrator-output](images/db-migrator-output.png)
> Initial seed data creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
{{ end }}
## Run the application
## Run the Application
{{ if UI == "MVC" }}
@ -320,7 +322,6 @@ Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
yarn start
```
Wait `Angular CLI` to launch `Webpack` dev-server with `BrowserSync`.
This will take care of compiling your `TypeScript` code, and automatically reloading your browser.
After it finishes, `Angular Live Development Server` will be listening on localhost:4200,
open your web browser and navigate to [localhost:4200](http://localhost:4200/)
@ -331,83 +332,18 @@ open your web browser and navigate to [localhost:4200](http://localhost:4200/)
{{ end }}
Enter **admin** as the username and **1q2w3E*** as the password to login to the application:
![bookstore-home](images/bookstore-home.png)
The application is up and running. You can start developing your application based on this startup template.
#### Mobile Development
ABP platform provide [React Native](https://reactnative.dev/) template to develop mobile applications.
>The solution includes the React Native application in the `react-native` folder as default. If you don't plan to develop a mobile application with React Native, you can ignore this step and delete the `react-native` folder.
The React Native application running on an Android emulator or a physical phone cannot connect to the backend on `localhost`. To fix this problem, it is necessary to run backend on the local IP.
{{ if Tiered == "No"}}
![React Native host project local IP entry](images/rn-host-local-ip.png)
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `SelfUrl` and `Authority` properties with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ else if Tiered == "Yes" }}
![React Native tiered project local IP entry](images/rn-tiered-local-ip.png)
* Open the `appsettings.json` in the `.IdentityServer` folder. Replace the `localhost` address on the `SelfUrl` property with your local IP address.
* Open the `launchSettings.json` in the `.IdentityServer/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
* Open the `appsettings.json` in the `.HttpApi.Host` folder. Replace the `localhost` address on the `Authority` property with your local IP address.
* Open the `launchSettings.json` in the `.HttpApi.Host/Properties` folder. Replace the `localhost` address on the `applicationUrl` properties with your local IP address.
{{ end }}
Run the backend as described in the [**Running the HTTP API Host (server-side)**](#running-the-http-api-host-server-side) section.
> React Native application does not trust the auto-generated .NET HTTPS certificate, you should use the HTTP during development.
Go to the `react-native` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work in most cases):
```bash
yarn
```
* Open the `Environment.js` in the `react-native` folder and replace the `localhost` address on the `apiUrl` and `issuer` properties with your local IP address as shown below:
![react native environment local IP](images/rn-environment-local-ip.png)
{{ if Tiered == "Yes" }}
> Make sure that `issuer` matches the running address of the `.IdentityServer` project, `apiUrl` matches the running address of the `.HttpApi.Host` project.
{{else}}
> Make sure that `issuer` and `apiUrl` matches the running address of the `.HttpApi.Host` project.
{{ end }}
Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
```bash
yarn start
```
Wait Expo CLI to start. Expo CLI opens the management interface on the `http://localhost:19002/` address.
![expo-interface](images/rn-expo-interface.png)
In the above management interface, you can start the application with an Android emulator, an iOS simulator or a physical phone by the scan the QR code with the [Expo Client](https://expo.io/tools#client).
Enter **admin** as the username and **1q2w3E*** as the password to login to the application. The application is up and running. You can start developing your application based on this startup template.
> See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/), [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) documents on expo.io.
## Mobile Development
![React Native login screen on iPhone 11](images/rn-login-iphone.png)
When you create a new application, the solution includes `react-native` folder by default. This is a basic [React Native](https://reactnative.dev/) startup template to develop mobile applications integrated to your ABP based backends.
Enter **admin** as the username and **1q2w3E*** as the password to login to the application.
If you don't plan to develop a mobile application with React Native, you can safely delete the `react-native` folder.
The application is up and running. You can continue to develop your application based on this startup template.
> You can specifying the `--mobile none` option to the ABP CLI to not create the `react-native` folder in the beginning.
> The [application startup template](Startup-Templates/Application.md) includes the TenantManagement and Identity modules.
See the "[Getting Started with the React Native](Getting-Started-React-Native.md)" document to learn how to configure and run the React Native application.
## What's next?
## See Also
[Application development tutorial](Tutorials/Part-1.md)
* [Web Application Development Tutorial](Tutorials/Part-1.md)

2
docs/en/How-To/Azure-Active-Directory-Authentication-MVC.md

@ -136,7 +136,7 @@ And that's it, integration is completed. Keep on mind that you can connect any o
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization).
# FAQ

2
docs/en/How-To/Customize-Login-Page-MVC.md

@ -105,7 +105,7 @@ Just changed the `@model` to `Acme.BookStore.Web.Pages.Account.CustomLoginModel`
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization).
## See Also

2
docs/en/How-To/Customize-SignIn-Manager.md

@ -93,7 +93,7 @@ PreConfigure<IdentityBuilder>(identityBuilder =>
## The Source Code
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization).
You can find the source code of the completed example [here](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization).
## See Also

6
docs/en/Index.md

@ -1,6 +1,6 @@
# ABP Documentation
ABP is an **open source application framework** focused on ASP.NET Core based web application development, but also supports developing other types of applications.
ABP is an **open source application framework** focused on ASP.NET Core based web application development, but also supports developing other type of applications.
Explore the left navigation menu to deep dive in the documentation.
@ -16,6 +16,10 @@ If you want to start from scratch (with an empty project) then manually install
* [Console Application](Getting-Started-Console-Application.md)
* [ASP.NET Core Web Application](Getting-Started-AspNetCore-Application.md)
## Packages
ABP Framework is distributed as NuGet and NPM packages. See [this page](http://abp.io/packages) for the complete list of the packages.
## Source Code
ABP is hosted on GitHub. See [the source code](https://github.com/abpframework/abp).

2
docs/en/Repositories.md

@ -190,8 +190,6 @@ This method is suggested;
### Option-3: IAsyncQueryableExecuter
> Notice that this feature is available in the ABP Framework 3.0 and later. While it is also usable with older versions, it was providing a very limited set of methods.
`IAsyncQueryableExecuter` is a service that is used to execute an `IQueryable<T>` object asynchronously **without depending on the actual database provider**.
**Example: Inject & use the `IAsyncQueryableExecuter.ToListAsync()` method**

1
docs/en/Road-Map.md

@ -9,7 +9,6 @@ While we will **continue to add other exciting features**, we will work on the f
Beside this middle term goals, there are many features in the [backlog](https://github.com/abpframework/abp/milestone/2). Here, a list of some major items in the backlog;
* [#4098](https://github.com/abpframework/abp/issues/4098) / Blob Storing Azure provider.
* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a **gRPC integration** infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index))
* [#236](https://github.com/abpframework/abp/issues/236) Resource based authorization system
* [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities

6
docs/en/Samples/Index.md

@ -7,7 +7,7 @@ Here, a list of official samples built with the ABP Framework. Most of these sam
A complete solution to demonstrate how to build systems based on the microservice architecture.
* [The complete documentation for this sample](Microservice-Demo.md)
* [Source code](https://github.com/abpframework/abp/tree/dev/samples/MicroserviceDemo)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/MicroserviceDemo)
* [Microservice architecture document](../Microservice-Architecture.md)
### Book Store
@ -48,8 +48,10 @@ While there is no Razor Pages & MongoDB combination, you can check both document
* **Text Templates Demo**: Shows different use cases of the text templating system.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo)
* [Text templating documentation](../Text-Templating.md)
* **Stored Procedure Demo**: Demonstrates how to use stored procedures, database views and functions with best practices.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo)
* **Authentication Customization**: A solution to show how to customize the authentication for ASP.NET Core MVC / Razor Pages applications.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)
* Related "[How To](../How-To/Index.md)" documents:
* [Azure Active Directory Authentication](../How-To/Azure-Active-Directory-Authentication-MVC.md)
* [Customize the Login Page](../How-To/Customize-Login-Page-MVC.md)

2
docs/en/Samples/Microservice-Demo.md

@ -28,7 +28,7 @@ The diagram below shows the system:
### Source Code
You can get the source code from [the GitHub repository](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo).
You can get the source code from [the GitHub repository](https://github.com/abpframework/abp-samples/tree/master/MicroserviceDemo).
## Running the Solution

911
docs/en/Tutorials/Part-1.md

File diff suppressed because it is too large

1307
docs/en/Tutorials/Part-2.md

File diff suppressed because it is too large

1227
docs/en/Tutorials/Part-3.md

File diff suppressed because it is too large

248
docs/en/Tutorials/Part-4.md

@ -0,0 +1,248 @@
# Web Application Development Tutorial - Part 4: Integration Tests
````json
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF","Mongo"]
}
````
{{
if UI == "MVC"
UI_Text="mvc"
else if UI == "NG"
UI_Text="angular"
else
UI_Text="?"
end
if DB == "EF"
DB_Text="Entity Framework Core"
else if DB == "Mongo"
DB_Text="MongoDB"
else
DB_Text="?"
end
}}
## About This Tutorial
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
- [Part 1: Creating the project and book list page](Part-1.md)
- [Part 2: The book list page](Part-2.md)
- [Part 3: Creating, updating and deleting books](Part-3.md)
- **Part 4: Integration tests (this part)**
- [Part 5: Authorization](Part-5.md)
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## Test Projects in the Solution
This part covers the **server side** tests. There are several test projects in the solution:
![bookstore-test-projects-v2](./images/bookstore-test-projects-{{UI_Text}}.png)
Each project is used to test the related project. Test projects use the following libraries for testing:
* [Xunit](https://xunit.github.io/) as the main test framework.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) as the assertion library.
* [NSubstitute](http://nsubstitute.github.io/) as the mocking library.
{{if DB=="EF"}}
> The test projects are configured to use **SQLite in-memory** as the database. A separate database instance is created and seeded (with the data seed system) to prepare a fresh database for every test.
{{else if DB=="Mongo"}}
> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)** library is used to mock the MongoDB database. A separate database instance is created and seeded (with the data seed system) to prepare a fresh database for every test.
{{end}}
## Adding Test Data
If you had created a data seed contributor as described in the [first part](Part-1.md), the same data will be available in your tests. So, you can skip this section. If you haven't created the seed contributor, you can use the `BookStoreTestDataSeedContributor` to seed the same data to be used in the tests below.
## Testing the BookAppService
Create a test class named `BookAppService_Tests` in the `Acme.BookStore.Application.Tests` project:
````csharp
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit;
namespace Acme.BookStore.Books
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
}
}
}
````
* `Should_Get_List_Of_Books` test simply uses `BookAppService.GetListAsync` method to get and check the list of books.
* We can safely check the book "1984" by its name, because we know that this books is available in the database since we've added it in the seed data.
Add a new test method to the `BookAppService_Tests` class that creates a new **valid** book:
````csharp
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
````
Add a new test that tries to create an invalid book and fails:
````csharp
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
````
* Since the `Name` is empty, ABP will throw an `AbpValidationException`.
The final test class should be as shown below:
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
namespace Acme.BookStore.Books
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
}
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
}
}
````
Open the **Test Explorer Window** (use Test -> Windows -> Test Explorer menu if it is not visible) and **Run All** tests:
![bookstore-appservice-tests](./images/bookstore-appservice-tests.png)
Congratulations, the **green icons** indicates that the tests have been successfully passed!
## The Next Part
See the [next part](part-5.md) of this tutorial.

401
docs/en/Tutorials/Part-5.md

@ -0,0 +1,401 @@
# Web Application Development Tutorial - Part 5: Authorization
````json
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF","Mongo"]
}
````
{{
if UI == "MVC"
UI_Text="mvc"
else if UI == "NG"
UI_Text="angular"
else
UI_Text="?"
end
if DB == "EF"
DB_Text="Entity Framework Core"
else if DB == "Mongo"
DB_Text="MongoDB"
else
DB_Text="?"
end
}}
## About This Tutorial
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
- [Part 1: Creating the project and book list page](Part-1.md)
- [Part 2: The book list page](Part-2.md)
- [Part 3: Creating, updating and deleting books](Part-3.md)
- [Part 4: Integration tests](Part-4.md)
- **Part 5: Authorization (this part)**
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## Permissions
ABP Framework provides an [authorization system](../Authorization.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client.
### Permission Names
A permission must have a unique name (a `string`). The best way is to define it as a `const`, so we can reuse the permission name.
Open the `BookStorePermissions` class inside the `Acme.BookStore.Application.Contracts` project and change the content as shown below:
````csharp
namespace Acme.BookStore.Permissions
{
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
public static class Books
{
public const string Default = GroupName + ".Books";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
}
````
This is a hierarchical way of defining permission names. For example, "create book" permission name was defined as `BookStore.Books.Create`.
### Permission Definitions
You should define permissions before using them.
Open the `BookStorePermissionDefinitionProvider` class inside the `Acme.BookStore.Application.Contracts` project and change the content as shown below:
````csharp
using Acme.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace Acme.BookStore.Permissions
{
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));
var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create"));
booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit"));
booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<BookStoreResource>(name);
}
}
}
````
This class defines a **permission group** (to group permissions on the UI, will be seen below) and **4 permissions** inside this group. Also, **Create**, **Edit** and **Delete** are children of the `BookStorePermissions.Books.Default` permission. A child permission can be selected **only if the parent was selected**.
Finally, edit the localization file (`en.json` under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project) to define the localization keys used above:
````json
"Permission:BookStore": "Book Store",
"Permission:Books": "Book Management",
"Permission:Books.Create": "Creating new books",
"Permission:Books.Edit": "Editing the books",
"Permission:Books.Delete": "Deleting the books"
````
> Localization key names are arbitrary and no forcing rule. But we prefer the convention used above.
### Permission Management UI
Once you define the permissions, you can see them on the **permission management modal**.
Go to the *Administration -> Identity -> Roles* page, select *Permissions* action for the admin role to open the permission management modal:
![bookstore-permissions-ui](images/bookstore-permissions-ui.png)
Grant the permissions you want and save the modal.
## Authorization
Now, you can use the permissions to authorize the book management.
### Application Layer & HTTP API
Open the `BookAppService` class and add set the policy names as the permission names defined above:
````csharp
using System;
using Acme.BookStore.Permissions;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore.Books
{
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto>, //Used to create/update a book
IBookAppService //implement the IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
GetPolicyName = BookStorePermissions.Books.Default;
GetListPolicyName = BookStorePermissions.Books.Default;
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Delete;
}
}
}
````
Added code to the constructor. Base `CrudAppService` automatically uses these permissions on the CRUD operations. This makes the **application service** secure, but also makes the **HTTP API** secure since this service is automatically used as an HTTP API as explained before (see [auto API controllers](../API/Auto-API-Controllers.md)).
{{if UI == "MVC"}}
### Razor Page
While securing the HTTP API & the application service prevents unauthorized users to use the services, they can still navigate to the book management page. While they will get authorization exception when the page makes the first AJAX call to the server, we should also authorize the page for a better user experience and security.
Open the `BookStoreWebModule` and add the following code block inside the `ConfigureServices` method:
````csharp
Configure<RazorPagesOptions>(options =>
{
options.Conventions.AuthorizePage("/Books/Index", BookStorePermissions.Books.Default);
options.Conventions.AuthorizePage("/Books/CreateModal", BookStorePermissions.Books.Create);
options.Conventions.AuthorizePage("/Books/EditModal", BookStorePermissions.Books.Edit);
});
````
Now, unauthorized users are redirected to the **login page**.
#### Hide the New Book Button
The book management page has a *New Book* button that should be invisible if the current user has no *Book Creation* permission.
![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png)
Open the `Pages/Books/Index.cshtml` file and change the content as shown below:
````html
@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Permissions
@using Acme.BookStore.Web.Pages.Books
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Localization
@model IndexModel
@inject IStringLocalizer<BookStoreResource> L
@inject IAuthorizationService AuthorizationService
@section scripts
{
<abp-script src="/Pages/Books/Index.js"/>
}
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<abp-card-title>@L["Books"]</abp-card-title>
</abp-column>
<abp-column size-md="_6" class="text-right">
@if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create))
{
<abp-button id="NewBookButton"
text="@L["NewBook"].Value"
icon="plus"
button-type="Primary"/>
}
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="BooksTable"></abp-table>
</abp-card-body>
</abp-card>
````
* Added `@inject IAuthorizationService AuthorizationService` to access to the authorization service.
* Used `@if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create))` to check the book creation permission to conditionally render the *New Book* button.
### JavaScript Side
Books table in the book management page has an actions button for each row. The actions button includes *Edit* and *Delete* actions:
![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png)
We should hide an action if the current user has not granted for the related permission. Datatables row actions has a `visible` option that can be set to `false` to hide the action item.
Open the `Pages/Books/Index.js` inside the `Acme.BookStore.Web` project and add a `visible` option to the `Edit` action as shown below:
````js
{
text: l('Edit'),
visible: abp.auth.isGranted('BookStore.Books.Edit'), //CHECK for the PERMISSION
action: function (data) {
editModal.open({ id: data.record.id });
}
}
````
Do same for the `Delete` action:
````js
visible: abp.auth.isGranted('BookStore.Books.Delete')
````
* `abp.auth.isGranted(...)` is used to check a permission that is defined before.
* `visible` could also be get a function that returns a `bool` if the value will be calculated later, based on some conditions.
### Menu Item
Even we have secured all the layers of the book management page, it is still visible on the main menu of the application. We should hide the menu item if the current user has no permission.
Open the `BookStoreMenuContributor` class, find the code block below:
````csharp
context.Menu.AddItem(
new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
).AddItem(
new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/Books"
)
)
);
````
And replace this code block with the following:
````csharp
var bookStoreMenu = new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
);
context.Menu.AddItem(bookStoreMenu);
//CHECK the PERMISSION
if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/Books"
));
}
````
{{else if UI == "NG"}}
### Angular Guard Configuration
First step of the UI is to prevent unauthorized users to see the "Books" menu item and enter to the book management page.
Open the `/src/app/book/book-routing.module.ts` and replace with the following content:
````js
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard, PermissionGuard } from '@abp/ng.core';
import { BookComponent } from './book.component';
const routes: Routes = [
{ path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class BookRoutingModule {}
````
* Imported `AuthGuard` and `PermissionGuard` from the `@abp/ng.core`.
* Added `canActivate: [AuthGuard, PermissionGuard]` to the route definition.
Open the `/src/app/route.provider.ts` and add `requiredPolicy: 'BookStore.Books'` to the `/books` route. The `/books` route block should be following:
````js
{
path: '/books',
name: '::Menu:Books',
parentName: '::Menu:BookStore',
layout: eLayoutType.application,
requiredPolicy: 'BookStore.Books',
}
````
### Hide the New Book Button
The book management page has a *New Book* button that should be invisible if the current user has no *Book Creation* permission.
![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png)
Open the `/src/app/book/book.component.html` file and replace the create button HTML content as shown below:
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Create" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i>
<span>{{ '::NewBook' | abpLocalization }}</span>
</button>
````
* Just added `abpPermission="BookStore.Books.Create"` that hides the button if the current user has no permission.
### Hide the Edit and Delete Actions
Books table in the book management page has an actions button for each row. The actions button includes *Edit* and *Delete* actions:
![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png)
We should hide an action if the current user has not granted for the related permission.
Open the `/src/app/book/book.component.html` file and replace the edit and delete buttons contents as shown below:
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Edit" ngbDropdownItem (click)="editBook(row.id)">
{{ '::Edit' | abpLocalization }}
</button>
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Delete" ngbDropdownItem (click)="delete(row.id)">
{{ 'AbpAccount::Delete' | abpLocalization }}
</button>
````
* Added `abpPermission="BookStore.Books.Edit"` that hides the edit action if the current user has no editing permission.
* Added `abpPermission="BookStore.Books.Delete"` that hides the delete action if the current user has no delete permission.
{{end}}

BIN
docs/en/Tutorials/images/bookstore-book-and-booktype.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/en/Tutorials/images/bookstore-book-list-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
docs/en/Tutorials/images/bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/Tutorials/images/bookstore-dbmigrator-on-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/en/Tutorials/images/bookstore-edit-button-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/en/Tutorials/images/bookstore-edit-delete-actions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/en/Tutorials/images/bookstore-getlist-result-network.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/en/Tutorials/images/bookstore-index-js-file-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
docs/en/Tutorials/images/bookstore-javascript-proxy-console.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/en/Tutorials/images/bookstore-new-book-button-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/en/Tutorials/images/bookstore-new-book-button-small.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/en/Tutorials/images/bookstore-permissions-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/en/Tutorials/images/generated-proxies-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

71
docs/en/UI/Angular/Config-State.md

@ -201,77 +201,6 @@ this.config.dispatchGetAppConfiguration();
Note that **you do not have to call this method at application initiation**, because the application configuration is already being received from the server at start.
### How to Patch Route Configuration
The `dispatchPatchRouteByName` finds a route by its name and replaces its configuration in the `Store` with the new configuration passed as the second parameter.
```js
// this.config is instance of ConfigStateService
const newRouteConfig: Partial<ABP.Route> = {
name: "Home",
path: "home",
children: [
{
name: "Dashboard",
path: "dashboard"
}
]
};
this.config.dispatchPatchRouteByName("::Menu:Home", newRouteConfig);
// returns a state stream which emits after dispatch action is complete
```
### How to Add a New Route Configuration
The `dispatchAddRoute` adds a new route to the configuration state in the `Store`. For this, the route config should be passed as the parameter of the method.
```js
// this.config is instance of ConfigStateService
const newRoute: ABP.Route = {
name: "My New Page",
iconClass: "fa fa-dashboard",
path: "page",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName.MyNewPage"
};
this.config.dispatchAddRoute(newRoute);
// returns a state stream which emits after dispatch action is complete
```
The `newRoute` will be placed as at root level, i.e. without any parent routes and its url will be stored as `'/path'`.
If you want **to add a child route, you can do this:**
```js
import { eIdentityRouteNames } from '@abp/ng.identity';
// this.config is instance of ConfigStateService
const newRoute: ABP.Route = {
parentName: eIdentityRouteNames.IdentityManagement,
name: "My New Page",
iconClass: "fa fa-dashboard",
path: "page",
invisible: false,
order: 2,
requiredPolicy: "MyProjectName.MyNewPage"
};
this.config.dispatchAddRoute(newRoute);
// returns a state stream which emits after dispatch action is complete
```
The `newRoute` will then be placed as a child of the parent route named `eIdentityRouteNames.IdentityManagement` and its url will be set as `'/identity/page'`.
#### Route Configuration Properties
Please refer to `ABP.Route` type for all the properties you can pass to `dispatchSetEnvironment` in its parameter. It can be found in the [common.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/common.ts#L27).
### How to Set the Environment
The `dispatchSetEnvironment` places environment variables passed to it in the `Store` under the configuration state. Here is how it is used:

51
docs/en/UI/Angular/Custom-Setting-Page.md

@ -1,39 +1,34 @@
# Custom Setting Page
There are several settings tabs from different modules. You can add custom settings page to your project in 3 steps.
There are several settings tabs from different modules. You can add a custom setting page to your project.
1. Create a Component
1. Create a component with the following command:
```js
import { Select } from '@ngxs/store';
import { Component } from '@angular/core';
@Component({
selector: 'app-your-custom-settings',
template: `
custom-settings works!
`,
})
export class YourCustomSettingsComponent {
// Your component logic
}
```bash
yarn ng generate component my-settings
```
2. Add the `YourCustomSettingsComponent` to `declarations` and the `entryComponents` arrays in the `AppModule`.
3. Open the `app.component.ts` and add the below content to the `ngOnInit`
2. Open the `app.component.ts` and modify the file as shown below:
```js
import { addSettingTab } from '@abp/ng.theme.shared';
// ...
ngOnInit() {
addSettingTab({
component: YourCustomSettingsComponent,
name: 'Type here the setting tab title (you can type a localization key, e.g: AbpAccount::Login',
order: 4,
requiredPolicy: 'type here a policy key'
});
import { Component } from '@angular/core';
import { SettingTabsService } from '@abp/ng.core'; // imported SettingTabsService
import { MySettingsComponent } from './my-settings/my-settings.component'; // imported MySettingsComponent
@Component(/* component metadata */)
export class AppComponent {
constructor(private settingTabs: SettingTabsService) // injected MySettingsComponent
{
// added below
settingTabs.add([
{
name: 'MySettings',
order: 1,
requiredPolicy: 'policy key here',
component: MySettingsComponent,
},
]);
}
}
```

21
docs/en/UI/Angular/Localization.md

@ -146,16 +146,6 @@ Find [styles configuration in angular.json](https://angular.io/guide/workspace-c
"build": {
"options": {
"styles": [
{
"input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"inject": true,
@ -166,6 +156,16 @@ Find [styles configuration in angular.json](https://angular.io/guide/workspace-c
"inject": true,
"bundleName": "fontawesome-v4-shims.min"
},
{
"input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
},
"apps/dev-app/src/styles.scss"
],
}
@ -174,6 +174,7 @@ Find [styles configuration in angular.json](https://angular.io/guide/workspace-c
}
}
}
```
#### Step 2. Clear Lazy Loaded Fontawesome in AppComponent

175
docs/en/UI/Angular/Migration-Guide-v3.md

@ -145,42 +145,33 @@ export class AppModule {}
AppRoutingModule:
```js
import { DynamicLayoutComponent } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module')
.then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account')
.then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity')
.then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () => import('@abp/ng.tenant-management')
.then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () => import('@abp/ng.setting-management')
.then(m => m.SettingManagementModule.forLazy()),
},
],
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () =>
import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
];
@ -191,7 +182,23 @@ const routes: Routes = [
export class AppRoutingModule {}
```
> You may have noticed that we used `DynamicLayoutComponent` at top level route component. We made this change in order to avoid unnecessary renders and flickering. It is not mandatory, but we recommend doing the same in your app routing.
AppComponent:
```js
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
`,
})
export class AppComponent {}
```
> You may have noticed that we used `<abp-dynamic-layout>` instead of `<router-outlet>` in the AppComponent template. We made this change in order to avoid unnecessary renders and flickering. It is not mandatory, but we recommend doing the same in your AppComponent.
#### What to Do When Migrating?
@ -201,7 +208,7 @@ export class AppRoutingModule {}
- Call static `forRoot` method of `ThemeBasicModule` (or `ThemeLeptonModule` if commercial) and remove `SharedModule` from imports (unless you have added anything that is necessary for your root module in it).
- Import lazy ABP modules directly in app routing module (e.g. `() => import('@abp/ng.identity').then(...)`).
- Call static `forLazy` method of all lazy modules inside `then`, even if a configuration is not passed.
- [OPTIONAL] Add an empty parent route with `DynamicLayoutComponent` for better performance and UX.
- [OPTIONAL] Add the `<abp-dynamic-layout></abp-dynamic-layout>` to the AppComponent template and remove the `<router-outlet></router-outlet>` for better performance and UX.
### RoutesService
@ -237,9 +244,10 @@ Until v3, we had been using a custom component, `abp-table`, as the default tabl
As of ABP v3, we have switched to a battle-tested, well-executed data grid: [ngx-datatable](https://github.com/swimlane/ngx-datatable). All ABP modules will come with ngx-datatable already implemented in them. `ThemeSharedModule` already exports `NgxDatatableModule`. So, if you install the package by running `yarn add @swimlane/ngx-datatable` in your terminal, it will be available for use in all modules of your app.
For proper styling, you need to add the following in the styles section of your angular.json file:
For proper styling, you need to add the following in the styles section of your angular.json file (above all others):
```json
"styles": [
{
"input": "node_modules/@swimlane/ngx-datatable/index.css",
"inject": true,
@ -254,7 +262,9 @@ For proper styling, you need to add the following in the styles section of your
"input": "node_modules/@swimlane/ngx-datatable/themes/material.css",
"inject": true,
"bundleName": "ngx-datatable-material"
}
},
// other styles
]
```
Since `abp-table` is not dropped yet, modules previously built by ABP v2.x will not suddenly lose all their tables. Yet, they will look and feel different from built-in ABP v3 modules. Therefore, you will probably want to convert the tables in those modules to ngx-datatable. In order to decrease the amount of work required to convert an abp-table into ngx-datatable, we have modified the [ListService](./List-Service.md) to work well with ngx-datatable and [introduced](https://volosoft.com/blog/attribute-directives-to-avoid-repetition-in-angular-templates) two new directives: `NgxDatatableListDirective` and `NgxDatatableDefaultDirective`.
@ -355,6 +365,103 @@ Once you bind the injected `ListService` instance through `NgxDatatableListDirec
**Important Note:** The `abp-table` is not removed, but is deprecated and will be removed in the future. Please consider switching to ngx-datatable.
### Extensions System [COMMERCIAL]
The extensions system is open sourced now and is publicly available from `@abp/ng.theme.shared/extensions` package instead of `@volo/abp.commercial.ng.ui`. Also, according to the new structure of config packages, the configuration is given through `forLazy` static methods as described above.
#### What to Do When Migrating?
If you have never used the extensions system before, you do not have to do anything. If you have, then please check the documentation again to see what changed. Extension system itself works the same as before. The only changes are the package you import from and the static method and the module you pass your contributors to.
### Lepton Theme Logos [COMMERCIAL]
In ABP v2.x, Lepton had one light and one dark logo per color theme. We have realized we could make it work with only one light and one dark logo. So, we have changed how Lepton looks up logo images and now you just need to have a `logo-light.png` and a `logo-dark.png` in your project.
#### What to Do When Migrating?
If you have switched template logo PNGs before, the change is simple:
- Go to `/assets/images/logo` folder.
- Rename `theme1.png` as `logo-light.png` and `theme1-reverse.png` as `logo-dark.png`.
- Delete all other `theme*.png` files.
If you have replaced the logo component(s), the change is a little bit different, but still simple. The `LayoutStateService` has a two new members: `primaryLogoColor` and `secondaryLogoColor`. They have an observable stream of `'light'` and `'dark'` strings as value. You can consume their value in your custom logo component templates with the `async` pipe. Here is a complete example which covers both primary and secondary (account) layout logos.
```js
import { AddReplaceableComponent } from '@abp/ng.core';
import { CommonModule } from '@angular/common';
import { APP_INITIALIZER, Component, Injector, NgModule } from '@angular/core';
import { Store } from '@ngxs/store';
import { eAccountComponents } from '@volo/abp.ng.account';
import {
AccountLayoutComponent,
eThemeLeptonComponents,
LayoutStateService,
} from '@volo/abp.ng.theme.lepton';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({
template: `
<div class="account-brand p-4 text-center mb-1" *ngIf="isAccount; else link">
<ng-template [ngTemplateOutlet]="link"></ng-template>
</div>
<ng-template #link>
<a [style.background-image]="logoUrl | async" class="navbar-brand" routerLink="/"></a>
</ng-template>
`,
})
export class LogoComponent {
isAccount: boolean;
logoColor: Observable<'dark' | 'light'>;
get logoUrl() {
return this.logoColor.pipe(map(color => `url(/assets/images/logo/logo-${color}.png)`));
}
constructor(injector: Injector) {
const layout = injector.get(LayoutStateService);
this.isAccount = Boolean(injector.get(AccountLayoutComponent, false));
this.logoColor = this.isAccount ? layout.secondaryLogoColor : layout.primaryLogoColor;
}
}
@NgModule({
imports: [CommonModule],
declarations: [LogoComponent],
exports: [LogoComponent],
})
export class LogoModule {}
export const APP_LOGO_PROVIDER = [
{ provide: APP_INITIALIZER, useFactory: switchLogos, multi: true, deps: [Store] },
];
export function switchLogos(store: Store) {
return () => {
store.dispatch(
new AddReplaceableComponent({
component: LogoComponent,
key: eThemeLeptonComponents.Logo,
}),
);
store.dispatch(
new AddReplaceableComponent({
component: LogoComponent,
key: eAccountComponents.Logo,
}),
);
};
}
```
Just add `APP_LOGO_PROVIDER` to the providers of your root module (usually `AppModule`) and you will have a custom logo component adjusting to the theme colors.
### Deprecated Interfaces
Some interfaces have long been marked as deprecated and now they are removed.

73
docs/en/UI/Angular/Modifying-the-Menu.md

@ -3,9 +3,6 @@
The menu is inside the `ApplicationLayoutComponent` in the @abp/ng.theme.basic package. There are several methods for modifying the menu elements. This document covers these methods. If you would like to replace the menu completely, please refer to [Component Replacement documentation](./Component-Replacement.md) and learn how to replace a layout.
<!-- TODO: Replace layout replacement document with component replacement. Layout replacement document will be created.-->
## How to Add a Logo
The `logoUrl` property in the environment variables is the url of the logo.
@ -215,51 +212,65 @@ After the operations above, the new menu looks like below:
## How to Add an Element to Right Part of the Menu
The right part elements are stored in the `LayoutState` that is in the @abp/ng.theme.basic package.
The `dispatchAddNavigationElement` method of the `LayoutStateService` adds an element to the right part of the menu.
You can insert an element by adding your template to `app.component` and calling the `dispatchAddNavigationElement` method:
You can add elements to the right part of the menu by calling the `addItems` method of `NavItemsService`. It is a singleton service, i.e. provided in root, so you can inject and use it immediately.
```js
import { Layout, LayoutStateService } from '@abp/ng.theme.basic'; // added this line
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<!-- Added below content -->
<ng-template #search
><input type="search" placeholder="Search" class="bg-transparent border-0"
/></ng-template>
<input type="search" placeholder="Search" class="bg-transparent border-0 color-white" />
`,
})
export class AppComponent {
// Added ViewChild
@ViewChild('search', { static: false, read: TemplateRef }) searchElementRef: TemplateRef<any>;
export class MySearchInputComponent {}
constructor(private layout: LayoutStateService) {} // injected LayoutStateService
// Added ngAfterViewInit
ngAfterViewInit() {
const newElement = {
name: 'Search',
element: this.searchElementRef,
order: 1,
} as Layout.NavigationElement;
this.layout.dispatchAddNavigationElement(newElement);
@Component(/* component metadata */)
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.addItems([
{
id: 'MySearchInput',
order: 1,
component: MySearchInputComponent,
},
{
id: 'SignOutIcon',
html: '<i class="fas fa-sign-out-alt fa-lg text-white m-2"><i>',
action: () => console.log('Clicked the sign out icon'),
order: 101, // puts as last element
},
]);
}
}
```
This inserts a search input to the menu. The final UI looks like below:
This inserts a search input and a sign out icon to the menu. The final UI looks like below:
![navigation-menu-search-input](./images/navigation-menu-search-input.png)
## How to Remove an Element From Right Part of the Menu
> The default elements have an order of `100`. If you want to place a custom element before the defaults, assign an order number up to `99`. If you want to place a custom element after the defaults, assign orders starting from `101`. Finally, if you must place an item between the defaults, patch the default element orders as described below. A warning though: We may add another default element in the future and it too will have an order number of `100`.
## How to Patch or Remove an Right Part Element
The `patchItem` method of `NavItemsService` finds an element by its `id` property and replaces its configuration with the new configuration passed as the second parameter. Similarly, `removeItem` method finds an element and removes it.
```js
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.patchItem(eThemeBasicComponents.Languages, {
requiredPolicy: 'new policy here',
order: 1,
});
navItems.removeItem(eThemeBasicComponents.CurrentUser);
}
}
```
TODO
* Patched the languages dropdown element with new `requiredPolicy` and new `order`.
* Removed the current user dropdown element.
## What's Next

8
docs/en/UI/Angular/Permission-Management.md

@ -55,7 +55,9 @@ The directive can also be used as an attribute directive but we recommend to you
You can use `PermissionGuard` if you want to control authenticated user's permission to access to the route during navigation.
Add `requiredPolicy` to the `routes` property in your routing module.
* Import the PermissionGuard from @abp/ng.core.
* Add `canActivate: [PermissionGuard]` to your route object.
* Add `requiredPolicy` to the `data` property of your route in your routing module.
```js
import { PermissionGuard } from '@abp/ng.core';
@ -66,9 +68,7 @@ const routes: Routes = [
component: YourComponent,
canActivate: [PermissionGuard],
data: {
routes: {
requiredPolicy: 'AbpIdentity.Roles.Create',
},
requiredPolicy: 'YourProjectName.YourComponent', // policy key for your component
},
},
];

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

@ -32,7 +32,7 @@ A variable named `apiName` (available as of v2.4) is defined in each service. `a
The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below:
```js
import { AbpApplicationConfigurationService } from '../app/shared/services';
import { AbpApplicationConfigurationService } from '../abp/applicationconfiguration/services';
//...
export class HomeComponent{
@ -48,14 +48,14 @@ The Angular compiler removes the services that have not been injected anywhere f
### Models
The generated models match the DTOs in the back-end. Each model is generated as a class under the `src/app/*/shared/models` folder.
The generated models match the DTOs in the back-end. Each model is generated as a class under the `src/app/*/models` folder.
There are a few [base classes](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/dtos.ts) in the `@abp/ng.core` package. Some models extend these classes.
A class instance can be created as shown below:
```js
import { IdentityRoleCreateDto } from '../identity/shared/models';
import { IdentityRoleCreateDto } from '../identity/role/models'
//...
const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, isPublic: true})
```

BIN
docs/en/UI/Angular/images/custom-settings.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 106 KiB

BIN
docs/en/UI/Angular/images/generated-files-via-generate-proxy.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/UI/Angular/images/navigation-menu-search-input.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

25
docs/en/docs-nav.json

@ -26,19 +26,27 @@
"text": "Tutorials",
"items": [
{
"text": "Application Development",
"text": "Web Application Development",
"items": [
{
"text": "Part-1: Creating a new solution and listing items",
"text": "1: Creating the Server Side",
"path": "Tutorials/Part-1.md"
},
{
"text": "Part-2: CRUD operations",
"text": "2: The Book List Page",
"path": "Tutorials/Part-2.md"
},
{
"text": "Part-3: Integration tests",
"text": "3: Creating, Updating and Deleting Books",
"path": "Tutorials/Part-3.md"
},
{
"text": "4: Integration Tests",
"path": "Tutorials/Part-4.md"
},
{
"text": "5: Authorization",
"path": "Tutorials/Part-5.md"
}
]
}
@ -432,6 +440,15 @@
}
]
},
{
"text": "React Native",
"items": [
{
"text": "Getting Started",
"path": "Getting-Started-React-Native.md"
}
]
},
{
"text": "Common",
"items": [

177
docs/zh-Hans/Blog-Posts/2020-07-01 v3_0_Release/Post.md

@ -0,0 +1,177 @@
# ABP框架 v3.0 已发布
我们很高兴地宣布,**ABP框架**和**ABP商业版**3.0版已经发布.与常规的2周发布一个版本不同的是, 这个版本用了4周的时间.关闭了**119个[issue](https://github.com/abpframework/abp/issues?q=is%3Aopen+is%3Aissue+milestone%3A3.0)**,合并了**89个[pull request](https://github.com/abpframework/abp/pulls?q=is%3Aopen+is%3Apr+milestone%3A3.0)** 和主框架[仓库](https://github.com/abpframework/abp)中的**798次提交**.
由于这是一个**主要版本**,它也包括了一些**重大更改**.不要害怕,这些变化都容易对应,并且下面会详细解释.
> 详细更改日志请查看[GitHub的发布说明](https://github.com/abpframework/abp/releases/tag/3.0.0).
## ABP框架3.0有哪些新增内容?
这篇文章将只包括重要功能/更改.你可在[GitHub的发布说明](https://github.com/abpframework/abp/releases/tag/3.0.0)上看到所有的更改.
### Angular 10!
Angular 10刚刚[发布](https://blog.angular.io/version-10-of-angular-now-available-78960babd41),我们就立即迁移了[启动模板](https://docs.abp.io/en/abp/latest/Startup-Templates/Application)到Angular 10!所以,当你以Angular UI[创建一个新的解决方案](https://abp.io/get-started)时, 你将获得新Angular的优点.
我们为想要迁移到Angular 10的老版本项目准备了一份[迁移指南](https://github.com/abpframework/abp/blob/dev/docs/en/UI/Angular/Migration-Guide-v3.md).
### Oracle集成包
之前我们为使用Devart库, 基于EF Core for Oracle的应用程序创建了[一个集成包](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart), 因为官方的Oracle EF Core包还未支持EF Core 3.1. 现在它以[beta发布](https://www.nuget.org/packages/Oracle.EntityFrameworkCore/3.19.0-beta1).虽然是beta版,但我们已经创建了它的[集成包](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle),这样你就可以在你的应用程序中使用它.
更多细节请查看[文档](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Oracle).
### Azure BLOB存储提供器
在上一个版本中我们创建了一个[Blob存储系统](https://docs.abp.io/en/abp/latest/Blob-Storing), 包含一个文件系统和数据库存储提供器. 本次发布我们引入了Azure BLOB存储提供器.请查看[文档](https://docs.abp.io/en/abp/latest/Blob-Storing-Azure).
### 分布式缓存批量操作和新的Redis缓存包
ASP.NET Core的[标准IDistributeCache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed)接口不包含**批量操作**,如使用单独方法/服务器调用设置多个项目. ABP框架引入了新的方法可用于在ABP的`IDistributedCache<T>`接口中进行批量操作:
* GetManyAsync / GetMany
* SetManyAsync / SetMany
然后,我们需要为Redis缓存实现这些新方法并且[必须创建](https://github.com/abpframework/abp/issues/4483)一个Redis集成包用于扩展微软的实现.
这些方法也被ABP框架所使用, 用来缓存某个用户/角色/租户的设置,功能和权限,并且带来了**显著的性能提升**.
更多细节请查看[缓存文档](https://docs.abp.io/en/abp/latest/Caching).
### 为虚拟文件系统支持嵌入文件清单
虚拟文件系统现在支持在项目中使用`GenerateEmbeddedFilesManifest`来添加编译程序集中嵌入资源的**真实的文件/目录结构**.所以,你现在可以访问文件,而没有任何文件名的限制(以前,目录名称中的一些特殊字符,比如`.`在某些情况会有问题)
查看[文档](https://docs.abp.io/en/abp/latest/Virtual-File-System)学习如何利用新系统的优点.
### 新示例
基于社区的需求,我们准备了两个新的示例应用程序:
* [StoredProcedureDemo](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo)演示了如何在自定义仓储库中调用存储过程,视图和函数.
* [OrganizationUnitSample](https://github.com/abpframework/abp-samples/tree/master/OrganizationUnitSample)演示了如何为你的实体使用[Identity模块](https://docs.abp.io/en/abp/latest/Modules/Identity)的组织单元系统.
### DynamicStringLength 和 DynamicMaxLength 属性
当类被用作模型或[DTO](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects)时, 标准的`StringLength`和`MaxLength`数据注解属性用来验证类中的属性是很有用的. 但是, 就像任何其它的属性一样, 长度的值应该是一个**编译时**已知的字面值(常量).
**示例: 使用`StringLength`**
````csharp
public class CreateBookDto
{
public const int MaxNameLength = 128; //常量!
[StringLength(MaxNameLength)]
public string Name { get; set; }
}
````
ABP框架现在有`DynamicStringLength` 和 `DynamicMaxLength`属性允许在**运行时**确定长度.
**示例: 使用`DynamicStringLength`**
````csharp
public class CreateBookDto
{
public static int MaxNameLength { get; set; } = 128;
[DynamicStringLength(typeof(CreateBookDto), nameof(MaxNameLength))]
public string Name { get; set; }
}
````
`DynamicStringLength`通过类的**类型**和这个类的一个静态属性的**名字**来读取最大长度(也有一个最小长度的选项, 就像`StringLength`).
这样就允许你从配置中读取最大值并且在应用程序启动时设置(一般在[模块](https://docs.abp.io/en/abp/latest/Module-Development-Basics)的`PreConfigureServices`方法)
````csharp
CreateBookDto.MaxNameLength = 200;
````
[预建应用程序模块](https://docs.abp.io/en/abp/latest/Modules/Index)使用了这个功能,所以你现在可以覆盖这些模块中定义的属性的最大长度了.
### 自动分布式事件
ABP可以为所有的实体在创建,更新和删除时**自动发布分布式事件**. 这很有用因为在分布式系统中你通常会对这些基础事件感兴趣.
这个功能在v3.0中**已成熟并[文档化了](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus#pre-defined-events)**. 你可以轻松地配置一部分或全部要发布事件的实体.
### IAsyncQueryableExecuter
使用LINQ扩展方法,当你在查询时需要调用`ToListAsync()`,`FirstOrDefaultAsync()`... 不幸的是,这些方法并不是**标准的**LINQ扩展方法.它们定义在[Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore)包中(或在[MongoDB.Driver](https://www.nuget.org/packages/MongoDB.Driver/),如果你使用MongoDB的话).
所以,如果你想使用异步扩展方法, 你需要依赖这个包.这样做破坏了分层,并且使你的应用程序或领域层依赖于EF Core / MongoDB的包.
`IAsyncQueryableExecuter`是ABP框架定义的服务, 用来**不依赖特定提供器地执行异步查询**(EF Core / MongoDB包).
查看[文档](https://docs.abp.io/en/abp/latest/Repositories#iqueryable-async-operations)来获取细节和了解我们的建议.
### API 文档
现在我们为每次发布ABP框架和模块都公开[API文档](https://docs.abp.io/api-docs/abp/3.0/api/index.html).所以,你可以更容易地查看ABP框架的类.点击[文档](https://docs.abp.io/en/abp/latest/)导航菜单中的**API文档**的链接.
### 包列表
我们[创建了一个页面](http://abp.io/packages)列出所有ABP相关的官方NuGet和NPM包.
### 其他
* 为Identity Server实现了前端通道的服务端客户[登出](https://identityserver4.readthedocs.io/en/latest/topics/signout.html)
* `abp.currentUser`(Angular UI中的`CurrentUser`服务)现在有一个包含当前用户的角色名的`roles`数组.
* 升级所有的Nu​​Get和NPM软件包的依赖.
* 引入了`ReadOnlyAppService`基类(只包含get操作), 做为`CrudAppService`基类(其中有get,create,update和delete操作)的补充.
其他更新请查看[GitHub发行说明](https://github.com/abpframework/abp/releases/tag/3.0.0).
## ABP商业版3.0有哪些新增内容?
除了所有ABP框架的功能以外,ABP商业版同样在本次发布中有一些额外的功能.本节介绍3.0版本中[ABP商业版](https://commercial.abp.io/)的亮点.
### 新的文件管理模块
我们创建了一个用于在应用程序中存储和管理文件的新模块.这个新模块基于[BLOB存储系统](https://docs.abp.io/en/abp/latest/Blob-Storing),因此它可以使用不同的存储提供器来存储文件内容.
**示例截图**
![file-management-ui](file-management-ui.png)
您可以上传,下载和以分层文件夹结构组织文件.它也兼容多租户,你可以为租户限制总文件大小.在未来的版本中,我们将致力于“共享”系统, 用于以一个更可控的方式在用户之间或通过公开链接与你的客户共享文件.
> 文件管理模块是目前仅适用于MVC / Razor Pages UI.我们正工作于Angular UI,它会在未来的版本中发布.
## 重大更改
由于这是一个主要版本,我们重新设计了一些API,并引入了一些"容易解决"的重大更改.
### ABP框架
* 更改了一些预建应用程序模块的**常量**为静态属性, 这样你就可在你的代码中对它们修改. 如果你在属性中使用了这些常量,那么请使用上面介绍的`DynamicStringLength`.
* 更改`ConcurrencyStamp`最大长度为40. 你需要在更新ABP框架后**添加一个数据库迁移**并且更新数据库.
* NPM软件包的依赖关系使用`~`代替`^`,为了更加稳定.
### ABP商业版
* 更改了应用程序标识的文件名.此前,它为每个主题使用了单独的标识文件,如`theme1.png`,`theme1-reverse.png`,`theme2.png`,`theme2-reverse.png`(...`6`).现在,我们只有两个标识文件:`logo-light.png`和`logo-dark.png`.所以,重命名MVC UI的`wwwroot/images/logo/`和Angular UI的`/src/assets/images/logo/`下的标识.
* 我们也增加了ABP商业版的[API文档](https://docs.abp.io/api-docs/commercial/3.0/api/index.html).
> **此外,Angular UI请查看[迁移指南](https://github.com/abpframework/abp/blob/dev/docs/en/UI/Angular/Migration-Guide-v3.md)**.
## 已知问题
* 3.0.0版本有一个分层架构的问题.查看[这个issue](https://github.com/abpframework/abp/pull/4564)为你的应用程序解决它,直到我们发布V3.0.1.
## 关于未来的版本
我们将继续在每两个星期发布一个新的小功能版本.所以,下一个预计发布日期是**2020年7月16日**的**3.1**版本.
在接下来的几个版本中,我们将专注于**Blazor UI**,如[路线图](https://docs.abp.io/en/abp/latest/Road-Map)中承诺的.我们将继续改进文档,创建示例,添加其他新功能和改善.关注[ABP框架Twitter账号](https://twitter.com/abpframework)获取最新消息...
## 彩蛋: 文章!
除了开发我们的产品,我们的团队一直在不断书写各种主题的文章/教程.你可能感兴趣的最新文章:
* [Angular 10有哪些新增内容?](https://volosoft.com/blog/what-is-new-in-angular-10)
* [在分布式架构中使用ABP, SignalR和RabbitMQ构建实时消息系统](https://volosoft.com/blog/RealTime-Messaging-Distributed-Architecture-Abp-SingalR-RabbitMQ)
* [如何在Angular模板中使用Attribute Directives来避免重复](https://volosoft.com/blog/attribute-directives-to-avoid-repetition-in-angular-templates)

BIN
docs/zh-Hans/Blog-Posts/2020-07-01 v3_0_Release/file-management-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

2
docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md

@ -136,7 +136,7 @@ private void ConfigureAuthentication(ServiceConfigurationContext context, IConfi
## 本文的源代码
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)找到已完成的示例源码.
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)找到已完成的示例源码.
# FAQ

2
docs/zh-Hans/How-To/Customize-Login-Page-MVC.md

@ -105,7 +105,7 @@ public class CustomLoginModel : LoginModel
## 本文的源代码
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)找到已完成的示例源码.
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)找到已完成的示例源码.
## 另请参阅

2
docs/zh-Hans/How-To/Customize-SignIn-Manager.md

@ -93,7 +93,7 @@ PreConfigure<IdentityBuilder>(identityBuilder =>
## 本文的源代码
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)找到已完成的示例源码.
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)找到已完成的示例源码.
## 另请参阅

4
docs/zh-Hans/Repositories.md

@ -248,6 +248,4 @@ ABP框架使用实际数据库提供程序的API异步执行查询.虽然这不
* 如果你正在构建一个没有数据库提供程序集成包的**可重用库**,但是在某些情况下需要执行 `IQueryable<T>`对象.
For example, ABP Framework uses the `IAsyncQueryableExecuter` in the `CrudAppService` base class (see the [application services](Application-Services.md) document).
例如,ABP框架在 `CrudAppService` 基类中(参阅[应用程序](Application-Services.md)文档)使用 `IAsyncQueryableExecuter`.
例如,ABP框架在 `CrudAppService` 基类中(参阅[应用程序](Application-Services.md)文档)使用 `IAsyncQueryableExecuter`.

2
docs/zh-Hans/Samples/Index.md

@ -49,7 +49,7 @@
* [源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo)
* [文本模板文档](../Text-Templating.md)
* **自定义认证**: 如何为ASP.NET Core MVC / Razor Pages应用程序自定义身份验证的解决方案.
* [源码](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)
* [源码](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)
* 相关 "[How To](../How-To/Index.md)" 文档:
* [Azure Active Directory 认证](../How-To/Azure-Active-Directory-Authentication-MVC.md)
* [自定义登录页面](../How-To/Customize-Login-Page-MVC.md)

5
docs/zh-Hans/Tutorials/Part-1.md

@ -675,7 +675,7 @@ namespace Acme.BookStore.Web.Menus
<h2>@L["Book"]</h2>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="BookTable">
<abp-table striped-rows="true" id="BooksTable">
<thead>
<tr>
<th>@L["Name"]</th>
@ -704,7 +704,8 @@ namespace Acme.BookStore.Web.Menus
````js
$(function () {
var dataTable = $('#BookTable').DataTable(abp.libs.datatables.normalizeConfiguration({
var l = abp.localization.getResource('BookStore');
var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({
ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList),
columnDefs: [
{ data: "name" },

2
docs/zh-Hans/docs-nav.json

@ -34,7 +34,7 @@
"path": "Tutorials/Part-1.md"
},
{
"text": "第一章: 增删改查操作",
"text": "第二章: 增删改查操作",
"path": "Tutorials/Part-2.md"
},
{

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -86,7 +86,9 @@
</div>
}
@(await Component.InvokeAsync<PageAlertsViewComponent>())
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.First, StandardLayouts.Account)
@RenderBody()
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.Last, StandardLayouts.Account)
</abp-column>
</abp-row>
</div>
@ -102,4 +104,4 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Account)
</body>
</html>
</html>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml

@ -64,7 +64,9 @@
@RenderSection("content_toolbar", false)
</div>
</div>
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.First, StandardLayouts.Application)
@RenderBody()
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.Last, StandardLayouts.Application)
</div>
<abp-script-bundle name="@BasicThemeBundles.Scripts.Global" />
@ -78,4 +80,4 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Application)
</body>
</html>
</html>

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml

@ -59,7 +59,9 @@
<div class="@containerClass">
@(await Component.InvokeAsync<PageAlertsViewComponent>())
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.First, StandardLayouts.Empty)
@RenderBody()
@await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.Last, StandardLayouts.Empty)
</div>
<abp-script-bundle name="@BasicThemeBundles.Scripts.Global" />
@ -73,4 +75,4 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Empty)
</body>
</html>
</html>

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Components/LayoutHook/LayoutHooks.cs

@ -13,5 +13,11 @@
public const string First = "Body.First";
public const string Last = "Body.Last";
}
public static class PageContent
{
public const string First = "PageContent.First";
public const string Last = "PageContent.Last";
}
}
}

34
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs

@ -11,8 +11,10 @@ using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.DependencyInjection;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using Volo.Abp.Localization;
using Volo.Abp.Reflection;
namespace Volo.Abp.Cli.Commands
@ -51,7 +53,7 @@ namespace Volo.Abp.Cli.Commands
var apiUrl = commandLineArgs.Options.GetOrNull(Options.ApiUrl.Short, Options.ApiUrl.Long);
if (string.IsNullOrWhiteSpace(apiUrl))
{
var environmentJson = File.ReadAllText("projects/dev-app/src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " ");
var environmentJson = File.ReadAllText("src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " ");
var environment = JObject.Parse(environmentJson);
apiUrl = environment["apis"]["default"]["url"].ToString();
}
@ -292,7 +294,7 @@ namespace Volo.Abp.Cli.Commands
if (firstType == "List" && !File.Exists(secondTypeModelPath))
{
secondType = "any";
}
}
serviceFileText.AppendLine(
firstType == "List"
@ -320,13 +322,15 @@ namespace Volo.Abp.Cli.Commands
"String" => "string",
"IActionResult" => "void",
"ActionResult" => "void",
"Int64" => "number",
"Int32" => "number",
_ => type
};
};
serviceFileText.AppendLine(
$" {actionName}({parametersText}): Observable<{type}> {{");
if (type != "void" && type != "string")
if (type != "void" && type != "string" && type != "number")
{
secondTypeList.Add(type);
}
@ -501,6 +505,11 @@ namespace Volo.Abp.Cli.Commands
var path = output + $"{outputPrefix}/{rootPath}/{controllerPathName}/models/{typeModelName}";
if (File.Exists(path))
{
return null;
}
var modelFileText = new StringBuilder();
var baseType = (string)type["baseType"];
@ -801,13 +810,16 @@ namespace Volo.Abp.Cli.Commands
return value;
}
return Regex.Replace(
value,
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
"-$1",
RegexOptions.Compiled)
.Trim()
.ToLower();
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
return Regex.Replace(
value,
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
"-$1",
RegexOptions.Compiled)
.Trim()
.ToLower();
}
}
}

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs

@ -10,9 +10,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App
{
var fileEntries = context.Files.Where(x =>
!x.IsDirectory &&
(x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.ts", StringComparison.InvariantCultureIgnoreCase) ||
x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.hmr.ts", StringComparison.InvariantCultureIgnoreCase) ||
x.Name.EndsWith("angular/projects/dev-app/src/environments/environment.prod.ts", StringComparison.InvariantCultureIgnoreCase))
(x.Name.EndsWith("angular/src/environments/environment.ts", StringComparison.InvariantCultureIgnoreCase) ||
x.Name.EndsWith("angular/src/environments/environment.hmr.ts", StringComparison.InvariantCultureIgnoreCase) ||
x.Name.EndsWith("angular/src/environments/environment.prod.ts", StringComparison.InvariantCultureIgnoreCase))
)
.ToList();

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -70,11 +70,18 @@ namespace Volo.Abp.Cli.ProjectModification
if (includePreviews)
{
await CreateNpmrcFileAsync(Path.GetDirectoryName(file.Key));
RunNpmInstall(fileDirectory);
}
else if (switchToStable)
{
await DeleteNpmrcFileAsync(Path.GetDirectoryName(file.Key));
}
if (await NpmrcFileExistAsync(fileDirectory))
{
RunNpmInstall(fileDirectory);
}
else
{
RunYarn(fileDirectory);
}
@ -93,15 +100,20 @@ namespace Volo.Abp.Cli.ProjectModification
await Task.CompletedTask;
}
private static async Task<bool> NpmrcFileExistAsync(string directoryName)
{
return File.Exists(Path.Combine(directoryName, ".npmrc"));
}
private async Task CreateNpmrcFileAsync(string directoryName)
{
var fileName = Path.Combine(directoryName, ".npmrc");
var abpRegistry = "@abp:registry=https://www.myget.org/F/abp-nightly/npm";
var voloRegistry = await GetVoloRegistryAsync();
if (File.Exists(fileName))
if (await NpmrcFileExistAsync(directoryName))
{
var fileContent = File.ReadAllText(fileName);
if (!fileContent.Contains(abpRegistry))
@ -237,7 +249,7 @@ namespace Volo.Abp.Cli.ProjectModification
_fileVersionStorage[package.Name] = newVersionWithPrefix;
return $"~{newVersionWithPrefix}";
return newVersionWithPrefix;
}
protected virtual List<JProperty> GetAbpPackagesFromPackageJson(JObject fileObject)

5
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -491,6 +491,11 @@ namespace Volo.Abp.EntityFrameworkCore
return;
}
if (!typeof(IEntity).IsAssignableFrom(typeof(TEntity)))
{
return;
}
modelBuilder.Entity<TEntity>().ConfigureByConvention();
ConfigureGlobalFilters<TEntity>(modelBuilder, mutableEntityType);

6
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs

@ -123,7 +123,7 @@ namespace Volo.Abp.IdentityModel
RequireHttps = configuration.RequireHttps
}
};
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage(request);
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage?.Invoke(request);
return await httpClient.GetDiscoveryDocumentAsync(request);
}
}
@ -165,7 +165,7 @@ namespace Volo.Abp.IdentityModel
UserName = configuration.UserName,
Password = configuration.UserPassword
};
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage(request);
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage?.Invoke(request);
AddParametersToRequestAsync(configuration, request);
@ -183,7 +183,7 @@ namespace Volo.Abp.IdentityModel
ClientId = configuration.ClientId,
ClientSecret = configuration.ClientSecret
};
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage(request);
IdentityModelHttpRequestMessageOptions.ConfigureHttpRequestMessage?.Invoke(request);
AddParametersToRequestAsync(configuration, request);

38
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -19,7 +19,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{
public class MongoDbRepository<TMongoDbContext, TEntity>
: RepositoryBase<TEntity>,
IMongoDbRepository<TEntity>
IMongoDbRepository<TEntity>,
IMongoQueryable<TEntity>
where TMongoDbContext : IAbpMongoDbContext
where TEntity : class, IEntity
{
@ -167,7 +168,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
}
public override async Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
@ -322,6 +323,35 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{
throw new AbpDbConcurrencyException("Database operation expected to affect 1 row but actually affected 0 row. Data may have been modified or deleted since entities were loaded. This exception has been thrown on optimistic concurrency check.");
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <returns></returns>
public QueryableExecutionModel GetExecutionModel()
{
return GetMongoQueryable().GetExecutionModel();
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public IAsyncCursor<TEntity> ToCursor(CancellationToken cancellationToken = new CancellationToken())
{
return GetMongoQueryable().ToCursor(cancellationToken);
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<IAsyncCursor<TEntity>> ToCursorAsync(CancellationToken cancellationToken = new CancellationToken())
{
return GetMongoQueryable().ToCursorAsync(cancellationToken);
}
}
public class MongoDbRepository<TMongoDbContext, TEntity, TKey>
@ -331,7 +361,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
where TEntity : class, IEntity<TKey>
{
public IMongoDbRepositoryFilterer<TEntity, TKey> RepositoryFilterer { get; set; }
public MongoDbRepository(IMongoDbContextProvider<TMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
@ -376,4 +406,4 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp);
}
}
}
}

7
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider.cs

@ -8,6 +8,7 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Linq;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.MongoDB
{
@ -15,12 +16,12 @@ namespace Volo.Abp.MongoDB
{
public bool CanExecute<T>(IQueryable<T> queryable)
{
return queryable.Provider.GetType().Namespace?.StartsWith("MongoDB") ?? false;
return ProxyHelper.UnProxy(queryable) is IMongoQueryable<T>;
}
protected IMongoQueryable<T> GetMongoQueryable<T>(IQueryable<T> queryable)
protected virtual IMongoQueryable<T> GetMongoQueryable<T>(IQueryable<T> queryable)
{
return (IMongoQueryable<T>) queryable;
return ProxyHelper.UnProxy(queryable).As<IMongoQueryable<T>>();
}
public Task<bool> ContainsAsync<T>(IQueryable<T> queryable, T item, CancellationToken cancellationToken = default)

76
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/MongoDbAsyncQueryableProvider_Tests.cs

@ -0,0 +1,76 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.Testing;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.MongoDB.Repositories
{
[Collection(MongoTestCollection.Name)]
public class MongoDbAsyncQueryableProvider_Tests : TestAppTestBase<AbpMongoDbTestModule>
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IRepository<Person, Guid> _personRepository;
private readonly MongoDbAsyncQueryableProvider _mongoDbAsyncQueryableProvider;
public MongoDbAsyncQueryableProvider_Tests()
{
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
_personRepository = GetRequiredService<IRepository<Person, Guid>>();
_mongoDbAsyncQueryableProvider = GetRequiredService<MongoDbAsyncQueryableProvider>();
}
[Fact]
public void CanExecute()
{
_mongoDbAsyncQueryableProvider.CanExecute(_personRepository).ShouldBeTrue();
_mongoDbAsyncQueryableProvider.CanExecute(_personRepository.WithDetails()).ShouldBeTrue();
}
[Fact]
public async Task FirstOrDefaultAsync()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _mongoDbAsyncQueryableProvider.FirstOrDefaultAsync(_personRepository.Where(p => p.Name == "Douglas"))).ShouldNotBeNull();
await uow.CompleteAsync();
}
}
[Fact]
public async Task AnyAsync()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _mongoDbAsyncQueryableProvider.AnyAsync(_personRepository, p => p.Name == "Douglas")).ShouldBeTrue();
await uow.CompleteAsync();
}
}
[Fact]
public async Task CountAsync()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _mongoDbAsyncQueryableProvider.CountAsync(_personRepository.Where(p => p.Name == "Douglas"))).ShouldBeGreaterThan(0);
await uow.CompleteAsync();
}
}
[Fact]
public async Task LongCountAsync()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _mongoDbAsyncQueryableProvider.LongCountAsync(_personRepository)).ShouldBeGreaterThan(0);
await uow.CompleteAsync();
}
}
//More MongoDbAsyncQueryableProvider's method test.
}
}

10
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver.Linq;
using Shouldly;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
@ -12,6 +13,15 @@ namespace Volo.Abp.MongoDB.Repositories
[Collection(MongoTestCollection.Name)]
public class Repository_Basic_Tests : Repository_Basic_Tests<AbpMongoDbTestModule>
{
[Fact]
public void ToMongoQueryable_Test()
{
((IMongoQueryable<Person>) PersonRepository).ShouldNotBeNull();
PersonRepository.As<IMongoQueryable<Person>>().ShouldNotBeNull();
((IMongoQueryable<Person>) PersonRepository.Where(p => p.Name == "Douglas")).ShouldNotBeNull();
PersonRepository.Where(p => p.Name == "Douglas").As<IMongoQueryable<Person>>().ShouldNotBeNull();
}
[Fact]
public async Task Linq_Queries()
{

7
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs

@ -28,6 +28,13 @@ namespace Volo.Abp.TestApp.Testing
person.Phones.Count.ShouldBe(2);
}
[Fact]
public async Task GetListAsync()
{
var persons = await PersonRepository.GetListAsync();
persons.Count.ShouldBeGreaterThan(0);
}
[Fact]
public async Task GetAsync_With_Predicate()
{

7
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo.Abp.Account.Application.Contracts.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Account.Application.Contracts</AssemblyName>
<PackageId>Volo.Abp.Account.Application.Contracts</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -19,4 +20,8 @@
<ProjectReference Include="..\..\..\identity\src\Volo.Abp.Identity.Application.Contracts\Volo.Abp.Identity.Application.Contracts.csproj" />
</ItemGroup>
</Project>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

7
modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Account.Application</AssemblyName>
<PackageId>Volo.Abp.Account.Application</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -16,5 +17,9 @@
<ProjectReference Include="..\..\..\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.UI.Navigation\Volo.Abp.UI.Navigation.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Account.Web
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>("Volo.Abp.Account.Web");
options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>();
});
//TODO: Try to reuse from AbpIdentityAspNetCoreModule

5
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -12,6 +12,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Abp.Account.Web</RootNamespace>
<OutputType>Library</OutputType>
</PropertyGroup>
@ -32,4 +33,8 @@
<PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs

@ -37,7 +37,7 @@ namespace Volo.Abp.Account.Web
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAccountWebModule>("Volo.Abp.Account.Web");
options.FileSets.AddEmbedded<AbpAccountWebModule>();
});
Configure<AbpNavigationOptions>(options =>

12
modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml

@ -3,16 +3,20 @@
@using Volo.Abp.Account.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Web.Pages.Account
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@inject IThemeManager ThemeManager
@inject IHtmlLocalizer<AccountResource> L
@{
Layout = ThemeManager.CurrentTheme.GetApplicationLayout();
}
@section scripts {
<abp-script-bundle name="@typeof(LoggedOutModel).FullName">
<abp-script src="/Pages/Account/LoggedOut.js"/>
<abp-script src="/Pages/Account/LoggedOut.js" />
</abp-script-bundle>
}
@section styles {
<abp-style src="/Pages/Account/LoggedOut.css"/>
<abp-style src="/Pages/Account/LoggedOut.css" />
}
<abp-card>
@ -28,4 +32,4 @@
<iframe class="signout logoutiframe" src="@Model.SignOutIframeUrl"></iframe>
}
</abp-card-body>
</abp-card>
</abp-card>

7
modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj

@ -11,6 +11,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Abp.Account.Web</RootNamespace>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
@ -34,4 +35,8 @@
<ProjectReference Include="..\Volo.Abp.Account.HttpApi\Volo.Abp.Account.HttpApi.csproj" />
</ItemGroup>
</Project>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

17
modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo.Blogging.Admin.Application.Contracts.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Blogging.Admin.Application.Contracts</AssemblyName>
<PackageId>Volo.Blogging.Admin.Application.Contracts</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -16,16 +17,10 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\cs.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\de.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\en.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\nl.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\pl-PL.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\pt-BR.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\sl.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\tr.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\vi.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\zh-Hans.json" />
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\zh-Hant.json" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Blogging\Admin\Localization\Resources\Blogging\Admin\ApplicationContracts\*.json" />
</ItemGroup>
</Project>

1
modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/BloggingAdminApplicationContractsModule.cs

@ -14,6 +14,7 @@ namespace Volo.Blogging.Admin
{
options.FileSets.AddEmbedded<BloggingAdminApplicationContractsModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources

2
modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminWebModule.cs

@ -40,7 +40,7 @@ namespace Volo.Blogging.Admin
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BloggingAdminWebModule>("Volo.Blogging.Admin");
options.FileSets.AddEmbedded<BloggingAdminWebModule>();
});
context.Services.AddAutoMapperObjectMapper<BloggingAdminWebModule>();

5
modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj

@ -10,6 +10,7 @@
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Blogging.Admin</RootNamespace>
</PropertyGroup>
@ -22,6 +23,10 @@
<PackageReference Include="Markdig.Signed" Version="0.20.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Pages\**\*.css" />

6
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo.Blogging.Application.Contracts.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Blogging.Application.Contracts</AssemblyName>
<PackageId>Volo.Blogging.Application.Contracts</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -19,4 +20,9 @@
<ProjectReference Include="..\Volo.Blogging.Domain.Shared\Volo.Blogging.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

7
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo.Blogging.Domain.Shared.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Blogging.Domain.Shared</AssemblyName>
<PackageId>Volo.Blogging.Domain.Shared</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -18,5 +19,9 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs

@ -39,7 +39,7 @@ namespace Volo.Blogging
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BloggingWebModule>("Volo.Blogging");
options.FileSets.AddEmbedded<BloggingWebModule>();
});
context.Services.AddAutoMapperObjectMapper<BloggingWebModule>();

5
modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj

@ -10,6 +10,7 @@
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Blogging</RootNamespace>
</PropertyGroup>
@ -22,6 +23,10 @@
<PackageReference Include="Markdig.Signed" Version="0.20.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Pages\**\*.css" />

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo.Docs.Admin.Application.Contracts.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Docs.Admin.Application.Contracts</AssemblyName>
<PackageId>Volo.Docs.Admin.Application.Contracts</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -20,4 +21,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/docs/src/Volo.Docs.Admin.Web/DocsAdminWebModule.cs

@ -39,7 +39,7 @@ namespace Volo.Docs.Admin
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<DocsAdminWebModule>("Volo.Docs.Admin");
options.FileSets.AddEmbedded<DocsAdminWebModule>();
});
context.Services.AddAutoMapperObjectMapper<DocsAdminWebModule>();

5
modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj

@ -9,6 +9,7 @@
<PackageId>Volo.Docs.Admin.Web</PackageId>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Docs.Admin</RootNamespace>
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
</PropertyGroup>
@ -25,6 +26,10 @@
<ProjectReference Include="..\Volo.Docs.Admin.HttpApi\Volo.Docs.Admin.HttpApi.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.css" />
<EmbeddedResource Include="Pages\**\*.js" />

2
modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj

@ -7,6 +7,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Docs.Domain</AssemblyName>
<PackageId>Volo.Docs.Domain</PackageId>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -19,6 +20,7 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.5" />
<PackageReference Include="Octokit" Version="0.48.0" />
<PackageReference Include="NEST" Version="7.7.1" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>

1
modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs

@ -12,6 +12,7 @@ namespace Volo.Docs.Bundling
context.Files.AddIfNotContains("/libs/prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard.js");
context.Files.AddIfNotContains("/libs/prismjs/plugins/line-highlight/prism-line-highlight.js");
context.Files.AddIfNotContains("/libs/prismjs/components/prism-csharp.js");
context.Files.AddIfNotContains("/libs/prismjs/components/prism-json.js");
}
}
}

2
modules/docs/src/Volo.Docs.Web/DocsWebModule.cs

@ -45,7 +45,7 @@ namespace Volo.Docs
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<DocsWebModule>("Volo.Docs");
options.FileSets.AddEmbedded<DocsWebModule>();
});
var configuration = context.Services.GetConfiguration();

2
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

@ -105,6 +105,7 @@ namespace Volo.Docs.Pages.Documents.Project
Logger.LogWarning(exception.Message);
DocumentFound = false;
Response.StatusCode = 404;
return Page();
}
}
@ -123,6 +124,7 @@ namespace Volo.Docs.Pages.Documents.Project
{
Logger.LogWarning(e.Message);
ProjectFound = false;
Response.StatusCode = 404;
return Page();
}

2
modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj

@ -9,6 +9,7 @@
<PackageId>Volo.Docs.Web</PackageId>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Docs</RootNamespace>
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
</PropertyGroup>
@ -23,6 +24,7 @@
<ProjectReference Include="..\Volo.Docs.HttpApi\Volo.Docs.HttpApi.csproj" />
<PackageReference Include="Markdig.Signed" Version="0.20.0" />
<PackageReference Include="Scriban" Version="2.1.3" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>

7
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo.Abp.FeatureManagement.Domain.Shared.csproj

@ -5,6 +5,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -12,7 +13,11 @@
<EmbeddedResource Include="Volo\Abp\FeatureManagement\Localization\Domain\*.json" />
<content Remove="Volo\Abp\FeatureManagement\Localization\Domain\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs

@ -33,7 +33,7 @@ namespace Volo.Abp.FeatureManagement
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpFeatureManagementWebModule>("Volo.Abp.FeatureManagement");
options.FileSets.AddEmbedded<AbpFeatureManagementWebModule>();
});
context.Services.AddAutoMapperObjectMapper<AbpFeatureManagementWebModule>();

5
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj

@ -8,6 +8,7 @@
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace>Volo.Abp.FeatureManagement</RootNamespace>
</PropertyGroup>
@ -18,6 +19,10 @@
<Content Remove="Pages\**\*.css" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />

5
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo.Abp.Identity.Domain.Shared.csproj

@ -11,6 +11,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -24,4 +25,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs

@ -42,7 +42,7 @@ namespace Volo.Abp.Identity.Web
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpIdentityWebModule>("Volo.Abp.Identity.Web");
options.FileSets.AddEmbedded<AbpIdentityWebModule>();
});
context.Services.AddAutoMapperObjectMapper<AbpIdentityWebModule>();

5
modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj

@ -11,6 +11,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
@ -38,4 +39,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

5
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo.Abp.PermissionManagement.Domain.Shared.csproj

@ -11,6 +11,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -23,4 +24,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs

@ -31,7 +31,7 @@ namespace Volo.Abp.PermissionManagement.Web
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpPermissionManagementWebModule>("Volo.Abp.PermissionManagement.Web");
options.FileSets.AddEmbedded<AbpPermissionManagementWebModule>();
});
context.Services.AddAutoMapperObjectMapper<AbpPermissionManagementWebModule>();

5
modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj

@ -10,6 +10,7 @@
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
@ -26,4 +27,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

5
modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo.Abp.SettingManagement.Domain.Shared.csproj

@ -11,6 +11,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -19,6 +20,10 @@
<Content Remove="Volo\Abp\SettingManagement\Localization\Resources\AbpSettingManagement\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
</ItemGroup>

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

Loading…
Cancel
Save