diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json index d4e51eeadf..eb49443d6e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json @@ -8,6 +8,7 @@ "FrameworkDocumentation": "Framework documentation", "OfficialBlog": "Official blog", "CommercialHomePage": "Commercial home page", - "CommercialSupportWebSite": "Commercial support web site" + "CommercialSupportWebSite": "Commercial support web site", + "CommunityWebSite": "ABP community web site" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json index 9c36cd611b..e403e72cf6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json @@ -8,6 +8,7 @@ "FrameworkDocumentation": "Framework Dokümantasyon", "OfficialBlog": "Resmi blog", "CommercialHomePage": "Kurumsal ana sayfa", - "CommercialSupportWebSite": "Kurumsal destek web sitesi" + "CommercialSupportWebSite": "Kurumsal destek web sitesi", + "CommunityWebSite": "ABP topluluk web sitesi" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 9e5afc3b33..a07b35c66b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -102,7 +102,16 @@ "DoYouWantToCreateNewUser": "Do you want to create new user?", "MasterModules": "Master Modules", "OrganizationName": "Organization name", + "CreationDate": "Creation date", + "LicenseStart": "License start date", + "LicenseEndDate": "License end date", "OrganizationNamePlaceholder": "Organization name...", + "TotalQuestionCountPlaceholder": "Total question count...", + "RemainingQuestionCountPlaceholder": "Remaining question count...", + "LicenseTypePlaceholder": "License type...", + "CreationDatePlaceholder": "Creation date...", + "LicenseStartDatePlaceholder": "License start date...", + "LicenseEndDatePlaceholder": "License end date...", "UsernameOrEmail": "Username or email", "UsernameOrEmailPlaceholder": "Username or email...", "Member": "Member", @@ -156,6 +165,7 @@ "RemainingQuestionCount": "Remaining question count", "TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount must be greater than RemainingQuestionCount !", "QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount and RemainingQuestionCount must be zero or greater than zero !", - "UnlimitedQuestionCount": "Unlimited question count" + "UnlimitedQuestionCount": "Unlimited question count", + "Notes": "Notes" } } \ No newline at end of file diff --git a/configureawait.props b/configureawait.props index 5247c80ae0..791c3e2a57 100644 --- a/configureawait.props +++ b/configureawait.props @@ -1,8 +1,8 @@ - - - all + + + All runtime; build; native; contentfiles; analyzers diff --git a/docs/en/CLI.md b/docs/en/CLI.md index f68ac2ce2e..160bb549c3 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -115,6 +115,7 @@ abp update [options] * `--nuget`: Only updates NuGet packages. * `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default * `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. +* `--check-all`: Check the new version of each package separately. Default is `false`. ### add-package diff --git a/docs/en/Entity-Framework-Core-Oracle-Devart.md b/docs/en/Entity-Framework-Core-Oracle-Devart.md new file mode 100644 index 0000000000..23e660378c --- /dev/null +++ b/docs/en/Entity-Framework-Core-Oracle-Devart.md @@ -0,0 +1,63 @@ +# Switch to EF Core Oracle Devart Provider + +This document explains how to switch to the **Oracle** database provider for **[the application startup template](Startup-Templates/Application.md)** which comes with SQL Server provider pre-configured. + +> This document uses a paid library of [Devart](https://www.devart.com/dotconnect/oracle/) company, See [this document](Entity-Framework-Core-Oracle.md) for other options. + +## Replace the Volo.Abp.EntityFrameworkCore.SqlServer Package + +`.EntityFrameworkCore` project in the solution depends on the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package. Remove this package and add the same version of the [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) package. + +## Replace the Module Dependency + +Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCoreOracleDevartModule)` + +Also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`. + +## UseOracle() + +Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: + +* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. +* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. + + +In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block + +```csharp +var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); +``` + +with this one +```csharp +var builder = (DbContextOptionsBuilder) + new DbContextOptionsBuilder().UseOracle + ( + configuration.GetConnectionString("Default") + ); +``` + +> Depending on your solution structure, you may find more code files need to be changed. + +## Change the Connection Strings + +Oracle connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/oracle/ ) for details of Oracle connection string options. + +You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. + +## Re-Generate the Migrations + +The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default. +EF Core Migrations depend on the selected DBMS provider. Changing the DBMS provider, may not work with the existing migrations. + +* Delete the `Migrations` folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. +* Run `Add-Migration "Initial"` on the Package Manager Console window (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). + +This will scaffold a new migration for Oracle. + +Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. + +## Run the Application + +It is ready. Just run the application and enjoy coding. diff --git a/docs/en/Entity-Framework-Core-Oracle-Official.md b/docs/en/Entity-Framework-Core-Oracle-Official.md new file mode 100644 index 0000000000..8ae1a1bac0 --- /dev/null +++ b/docs/en/Entity-Framework-Core-Oracle-Official.md @@ -0,0 +1,58 @@ +# Switch to EF Core Oracle Provider + +This document explains how to switch to the **Oracle** database provider for **[the application startup template](Startup-Templates/Application.md)** which comes with SQL Server provider pre-configured. + +## Replace the Volo.Abp.EntityFrameworkCore.SqlServer Package + +`.EntityFrameworkCore` project in the solution depends on the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package. Remove this package and add the same version of the [Volo.Abp.EntityFrameworkCore.Oracle](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle) package. + +## Replace the Module Dependency + +Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCoreOracleModule)` + +Also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.Oracle;`. + +## UseOracle() + +Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: + +* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. +* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. + + +In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block + +```csharp +var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); +``` + +with this one (just changes `UseSqlServer(...)` to `UseOracle(...)`) +```csharp +var builder = new DbContextOptionsBuilder() + .UseOracle(configuration.GetConnectionString("Default")); +``` + +> Depending on your solution structure, you may find more code files need to be changed. + +## Change the Connection Strings + +Oracle connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/oracle/ ) for details of Oracle connection string options. + +You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. + +## Re-Generate the Migrations + +The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default. +EF Core Migrations depend on the selected DBMS provider. Changing the DBMS provider, may not work with the existing migrations. + +* Delete the `Migrations` folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. +* Run `Add-Migration "Initial"` on the Package Manager Console window (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). + +This will scaffold a new migration for Oracle. + +Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. + +## Run the Application + +It is ready. Just run the application and enjoy coding. diff --git a/docs/en/Entity-Framework-Core-Oracle.md b/docs/en/Entity-Framework-Core-Oracle.md index 6ac414ebce..02aaac305c 100644 --- a/docs/en/Entity-Framework-Core-Oracle.md +++ b/docs/en/Entity-Framework-Core-Oracle.md @@ -2,61 +2,9 @@ This document explains how to switch to the **Oracle** database provider for **[the application startup template](Startup-Templates/Application.md)** which comes with SQL Server provider pre-configured. -> This document uses a paid library of [Devart](https://www.devart.com/dotconnect/oracle/) company, because it is the only library for Oracle that supports EF Core 3.x. +ABP Framework provides integrations for two different Oracle packages. See one of the following documents based on your provider decision: -## Replace the Volo.Abp.EntityFrameworkCore.SqlServer Package - -`.EntityFrameworkCore` project in the solution depends on the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package. Remove this package and add the same version of the [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) package. - -## Replace the Module Dependency - -Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). - -## UseOracle() - -Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: - -* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. -* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. - - -In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block - -```csharp -var builder = new DbContextOptionsBuilder() - .UseSqlServer(configuration.GetConnectionString("Default")); -``` - -with this one -```csharp -var builder = (DbContextOptionsBuilder) - new DbContextOptionsBuilder().UseOracle - ( - configuration.GetConnectionString("Default") - ); -``` - -> Depending on your solution structure, you may find more code files need to be changed. - -## Change the Connection Strings - -Oracle connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/oracle/ ) for details of Oracle connection string options. - -You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. - -## Re-Generate the Migrations - -The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default. -EF Core Migrations depend on the selected DBMS provider. Changing the DBMS provider, may not work with the existing migrations. - -* Delete the `Migrations` folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. -* Run `Add-Migration "Initial"` on the Package Manager Console window (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). - -This will scaffold a new migration for Oracle. - -Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. - -## Run the Application - -It is ready. Just run the application and enjoy coding. +* **[Volo.Abp.EntityFrameworkCore.Oracle](Entity-Framework-Core-Oracle-Official.md)** package uses the official & free oracle driver (which is **currently in beta**). +* **[Volo.Abp.EntityFrameworkCore.Oracle.Devart](Entity-Framework-Core-Oracle-Devart.md)** package uses the commercial (paid) driver of [Devart](https://www.devart.com/) company. +> You can choose one of the package you want. If you don't know the differences of the packages, please search for it. ABP Framework only provides integrations it doesn't provide support for such 3rd-party libraries. diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 835c74385f..f7b062aa6e 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -754,8 +754,8 @@ It's end of this part. The final UI of this work is shown as below: It's time to create something visible and usable! There are some tools that we will use when developing ABP Angular frontend application: - [Angular CLI](https://angular.io/cli) will be used to create modules, components and services. -- [NGXS](https://ngxs.gitbook.io/ngxs/) will be used as the state management library. - [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) will be used as the UI component library. +- [ngx-datatable](https://swimlane.gitbook.io/ngx-datatable/) will be used as the datatable library. - [Visual Studio Code](https://code.visualstudio.com/) will be used as the code editor (you can use your favorite editor). #### Install NPM packages @@ -778,26 +778,56 @@ yarn ng generate module book --routing true #### Routing -Open the `app-routing.module.ts` file in `src\app` folder. Add the new `import` and add a route as shown below +Open the `app-routing.module.ts` file in `src\app` folder and add a route as shown below: ```js -import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; //==> added this line to imports <== +const routes: Routes = [ +// ... +// added a new route to the routes array + { + path: 'books', + loadChildren: () => import('./book/book.module').then(m => m.BookModule) + } +] +``` -//...added books path with the below to the routes array -{ - path: 'books', - component: ApplicationLayoutComponent, - loadChildren: () => import('./book/book.module').then(m => m.BookModule), - data: { - routes: { - name: '::Menu:Books', - iconClass: 'fas fa-book' - } as ABP.Route - }, +* We added a lazy-loaded route. See the [Lazy-Loading Feature Modules](https://angular.io/guide/lazy-loading-ngmodules#lazy-loading-feature-modules). + +Open the `route.provider.ts` file in `src\app` folder and replace the content as below: + +```js +import { RoutesService, eLayoutType } from '@abp/ng.core'; +import { APP_INITIALIZER } from '@angular/core'; + +export const APP_ROUTE_PROVIDER = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + //... + // added below element + { + path: '/books', + name: '::Menu:Books', + iconClass: 'fas fa-book', + order: 101, + layout: eLayoutType.application, + }, + ]); + }; } ``` -* The `ApplicationLayoutComponent` configuration sets the application layout to the new page. We added the `data` object. The `name` is the menu item name and the `iconClass` is the icon of the menu item. +* We added a new route element to show a navigation element labeled "Books" on the menu. + * `path` is the URL of the route. + * `name` is the menu item name. A Localization key can be passed. + * `iconClass` is the icon of the menu item. + * `order` is the order of the menu item. We define 101 to show the route after the "Administration" item. + * `layout` is the layout of the BooksModule's routes. `eLayoutType.application`, `eLayoutType.account` or `eLayoutType.empty` can be defined. + +For more information, see the [RoutesService document](https://docs.abp.io/en/abp/latest/UI/Angular/Modifying-the-Menu.md#via-routesservice). #### Book list component @@ -865,35 +895,6 @@ Open the browser and navigate to http://localhost:4200/books. We'll see **book-l ![Initial book list page](./images/bookstore-initial-book-list-page.png) -#### Create BookState - -Run the following command in the terminal to create a new state, named `BooksState`: - -```bash -npx @ngxs/cli --name book --directory src/app/book -``` - -* This command creates `book.state.ts` and `book.actions.ts` files in the `src/app/book/state` folder. See the [NGXS CLI documentation](https://www.ngxs.io/plugins/cli). - -Import the `BookState` to the `app.module.ts` in the `src/app` folder and then add the `BookState` to `forRoot` static method of `NgxsModule` as an array element of the first parameter of the method. - -```js -// ... -import { BookState } from './books/state/book.state'; //<== imported BookState ==> - -@NgModule({ - imports: [ - // other imports - - NgxsModule.forRoot([BookState]), //<== added BookState ==> - - //other imports - ], - // ... -}) -export class AppModule {} -``` - #### Generate proxies ABP CLI provides `generate-proxy` command that generates client proxies for your HTTP APIs to make easy to consume your services from the client side. Before running generate-proxy command, your host must be up and running. See the [CLI documentation](../CLI.md) @@ -910,109 +911,41 @@ The generated files looks like below: ![Generated files](./images/generated-proxies.png) -#### GetBooks Action - -Actions can either be thought of as a command which should trigger something to happen, or as the resulting event of something that has already happened. [See NGXS Actions documentation](https://www.ngxs.io/concepts/actions). - -Open the `book.actions.ts` file in `app/book/state` folder and replace the content below: - -```js -export class GetBooks { - static readonly type = '[Book] Get'; -} -``` - -#### Implement BookState - -Open the `book.state.ts` file in `app/book/state` folder and replace the content below: - -```js -import { PagedResultDto } from '@abp/ng.core'; -import { State, Action, StateContext, Selector } from '@ngxs/store'; -import { GetBooks } from './book.actions'; -import { BookService } from '../services'; -import { tap } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; -import { BookDto } from '../models'; - -export class BookStateModel { - public book: PagedResultDto; -} - -@State({ - name: 'BookState', - defaults: { book: {} } as BookStateModel, -}) -@Injectable() -export class BookState { - @Selector() - static getBooks(state: BookStateModel) { - return state.book.items || []; - } - - constructor(private bookService: BookService) {} - - @Action(GetBooks) - get(ctx: StateContext) { - return this.bookService.getListByInput().pipe( - tap((booksResponse) => { - ctx.patchState({ - book: booksResponse, - }); - }) - ); - } -} -``` - -* We added the book property to BookStateModel model. -* We added the `GetBooks` action that retrieves the book data via `BookService` that generated via ABP CLI and patches the state. -* `NGXS` requires to return the observable without subscribing it in the get function. - #### BookListComponent Open the `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: ```js +import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { BookDto, BookType } from '../models'; -import { GetBooks } from '../state/book.actions'; -import { BookState } from '../state/book.state'; +import { BookService } from '../services'; @Component({ selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], + providers: [ListService], }) export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; + book = { items: [], totalCount: 0 } as PagedResultDto; booksType = BookType; - loading = false; - - constructor(private store: Store) {} + constructor(public readonly list: ListService, private bookService: BookService) {} ngOnInit() { - this.get(); - } + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } } ``` -* We added the `get` function that updates store to get the books. -* See the [Dispatching actions](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions) and [Select](https://ngxs.gitbook.io/ngxs/concepts/select) on the `NGXS` documentation for more information on these `NGXS` features. +* We imported and injected the generated `BookService`. +* We implemented the [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service) that is a utility service to provide easy pagination, sorting, and search implementation. Open the `book-list.component.html` file in `app\book\book-list` folder and replace the content as below: @@ -1022,38 +955,31 @@ Open the `book-list.component.html` file in `app\book\book-list` folder and repl
- {%{{{ "::Menu:Books" | abpLocalization }}}%} + {%{{{ '::Menu:Books' | abpLocalization }}}%}
- - - - - {%{{{ "::Name" | abpLocalization }}}%} - {%{{{ "::Type" | abpLocalization }}}%} - {%{{{ "::PublishDate" | abpLocalization }}}%} - {%{{{ "::Price" | abpLocalization }}}%} - - - - - {%{{{ data.name }}}%} - {%{{{ booksType[data.type] }}}%} - {%{{{ data.publishDate | date }}}%} - {%{{{ data.price }}}%} - - + + + + + {%{{{ booksType[row.type] }}}%} + + + + + {%{{{ row.publishDate | date }}}%} + + + + + {%{{{ row.price | currency }}}%} + + +
``` @@ -1074,4 +1000,4 @@ In this tutorial we have applied the rules of official [Angular Style Guide](htt ### Next Part -See the [part 2](part-2.md) for creating, updating and deleting books. +See the [part 2](./Part-2.md) for creating, updating and deleting books. diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 8ac9e19615..d296335cfa 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -454,78 +454,50 @@ Run the application and try to delete a book. In this section, you will learn how to create a new modal dialog form to create a new book. -#### State definitions - -Open `book.action.ts` in `app\book\state` folder and replace the content as below: +#### Add a modal to BookListComponent -```js -import { CreateUpdateBookDto } from '../models'; //<== added this line ==> -export class GetBooks { - static readonly type = '[Book] Get'; -} +Open `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: -// added CreateUpdateBook class -export class CreateUpdateBook { - static readonly type = '[Book] Create Update Book'; - constructor(public payload: CreateUpdateBookDto) { } -} -``` +```js +import { ListService, PagedResultDto } from '@abp/ng.core'; +import { Component, OnInit } from '@angular/core'; +import { BookDto, BookType } from '../models'; +import { BookService } from '../services'; -* We imported the `CreateUpdateBookDto` model and created the `CreateUpdateBook` action. +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], + providers: [ListService], +}) +export class BookListComponent implements OnInit { + book = { items: [], totalCount: 0 } as PagedResultDto; -Open `book.state.ts` file in `app\book\state` folder and replace the content as below: + booksType = BookType; -```js -import { PagedResultDto } from '@abp/ng.core'; -import { State, Action, StateContext, Selector } from '@ngxs/store'; -import { GetBooks, CreateUpdateBook } from './book.actions'; // <== added CreateUpdateBook==> -import { BookService } from '../services'; -import { tap } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; -import { BookDto } from '../models'; + isModalOpen = false; // <== added this line ==> -export class BookStateModel { - public book: PagedResultDto; -} + constructor(public readonly list: ListService, private bookService: BookService) {} -@State({ - name: 'BookState', - defaults: { book: {} } as BookStateModel, -}) -@Injectable() -export class BookState { - @Selector() - static getBooks(state: BookStateModel) { - return state.book.items || []; - } + ngOnInit() { + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - constructor(private bookService: BookService) {} - - @Action(GetBooks) - get(ctx: StateContext) { - return this.bookService.getListByInput().pipe( - tap((bookResponse) => { - ctx.patchState({ - book: bookResponse, - }); - }) - ); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } - // added CreateUpdateBook action listener - @Action(CreateUpdateBook) - save(ctx: StateContext, action: CreateUpdateBook) { - return this.bookService.createByInput(action.payload); + // added createBook method + createBook() { + this.isModalOpen = true; } } ``` -* We imported `CreateUpdateBook` action and defined the `save` method that will listen to a `CreateUpdateBook` action to create a book. - -When the `SaveBook` action dispatched, the save method is being executed. It calls `createByInput` method of the `BookService`. +* We defined a variable called `isModalOpen` and `createBook` method. +* We added the `createBook` method. -#### Add a modal to BookListComponent Open `book-list.component.html` file in `books\book-list` folder and replace the content as below: @@ -534,19 +506,12 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
-
- {%{{{ '::Menu:Books' | abpLocalization }}}%} -
+
{%{{{ '::Menu:Books' | abpLocalization }}}%}
- +
- @@ -555,47 +520,40 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
- - - - - {%{{{ "::Name" | abpLocalization }}}%} - {%{{{ "::Type" | abpLocalization }}}%} - {%{{{ "::PublishDate" | abpLocalization }}}%} - {%{{{ "::Price" | abpLocalization }}}%} - - - - - {%{{{ data.name }}}%} - {%{{{ booksType[data.type] }}}%} - {%{{{ data.publishDate | date }}}%} - {%{{{ data.price }}}%} - - + + + + + {%{{{ booksType[row.type] }}}%} + + + + + {%{{{ row.publishDate | date }}}%} + + + + + {%{{{ row.price | currency }}}%} + + +
- -

{%{{{ '::NewBook' | abpLocalization }}}%}

-
+ +

{%{{{ '::NewBook' | abpLocalization }}}%}

+
- + - - - + + +
``` @@ -603,55 +561,6 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the * `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits. * We added `New book` button to the `AbpContentToolbar`. -Open `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: - -```js -import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; -import { BookDto, BookType } from '../models'; -import { GetBooks } from '../state/book.actions'; -import { BookState } from '../state/book.state'; - -@Component({ - selector: 'app-book-list', - templateUrl: './book-list.component.html', - styleUrls: ['./book-list.component.scss'], -}) -export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; - - booksType = BookType; - - loading = false; - - isModalOpen = false; // <== added this line ==> - - constructor(private store: Store) {} - - ngOnInit() { - this.get(); - } - - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); - } - - // added createBook method - createBook() { - this.isModalOpen = true; - } -} -``` - -* We added `isModalOpen = false` and `createBook` method. - You can open your browser and click **New book** button to see the new modal. ![Empty modal for new book](./images/bookstore-empty-new-book-modal.png) @@ -663,48 +572,43 @@ You can open your browser and click **New book** button to see the new modal. Open `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: ```js +import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { BookDto, BookType } from '../models'; -import { GetBooks } from '../state/book.actions'; -import { BookState } from '../state/book.state'; +import { BookService } from '../services'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // <== added this line ==> @Component({ selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], + providers: [ListService], }) export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; + book = { items: [], totalCount: 0 } as PagedResultDto; booksType = BookType; - loading = false; - isModalOpen = false; form: FormGroup; // <== added this line ==> - constructor(private store: Store, private fb: FormBuilder) {} // <== added FormBuilder ==> + constructor( + public readonly list: ListService, + private bookService: BookService, + private fb: FormBuilder // <== injected FormBuilder ==> + ) {} ngOnInit() { - this.get(); - } + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } createBook() { - this.buildForm(); //<== added this line ==> + this.buildForm(); // <== added this line ==> this.isModalOpen = true; } @@ -799,13 +703,10 @@ export class BookModule {} Open `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: ```js +import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { BookDto, BookType } from '../models'; -import { GetBooks } from '../state/book.actions'; -import { BookState } from '../state/book.state'; +import { BookService } from '../services'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; // <== added this line ==> @@ -813,37 +714,34 @@ import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], // <== added this line ==> + providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], // <== added a provide ==> }) export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; + book = { items: [], totalCount: 0 } as PagedResultDto; booksType = BookType; - //added bookTypeArr array + // <== added bookTypeArr array ==> bookTypeArr = Object.keys(BookType).filter( (bookType) => typeof this.booksType[bookType] === 'number' ); - loading = false; - isModalOpen = false; form: FormGroup; - constructor(private store: Store, private fb: FormBuilder) {} + constructor( + public readonly list: ListService, + private bookService: BookService, + private fb: FormBuilder + ) {} ngOnInit() { - this.get(); - } + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } createBook() { @@ -884,13 +782,10 @@ Now, you can open your browser to see the changes: Open `book-list.component.ts` file in `app\book\book-list` folder and replace the content as below: ```js +import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { BookDto, BookType } from '../models'; -import { GetBooks, CreateUpdateBook } from '../state/book.actions'; // <== added CreateUpdateBook ==> -import { BookState } from '../state/book.state'; +import { BookService } from '../services'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; @@ -898,11 +793,10 @@ import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], + providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; + book = { items: [], totalCount: 0 } as PagedResultDto; booksType = BookType; @@ -910,24 +804,22 @@ export class BookListComponent implements OnInit { (bookType) => typeof this.booksType[bookType] === 'number' ); - loading = false; - isModalOpen = false; form: FormGroup; - constructor(private store: Store, private fb: FormBuilder) {} + constructor( + public readonly list: ListService, + private bookService: BookService, + private fb: FormBuilder + ) {} ngOnInit() { - this.get(); - } + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } createBook() { @@ -950,19 +842,18 @@ export class BookListComponent implements OnInit { return; } - this.store.dispatch(new CreateUpdateBook(this.form.value)).subscribe(() => { + this.bookService.createByInput(this.form.value).subscribe(() => { this.isModalOpen = false; this.form.reset(); - this.get(); + this.list.get(); }); } } ``` -* We imported `CreateUpdateBook`. * We added `save` method -Open `book-list.component.html` in `app\book\book-list` folder and add the following `abp-button` to save the new book. +Open `book-list.component.html` in `app\book\book-list` folder, find the `` element and replace this element with the following to create a new book. ```html @@ -994,63 +885,24 @@ The final modal UI looks like below: ### Updating a book -#### CreateUpdateBook action - -Open the `book.actions.ts` in `app\book\state` folder and replace the content as below: - -```js -import { CreateUpdateBookDto } from '../models'; - -export class GetBooks { - static readonly type = '[Book] Get'; -} - -export class CreateUpdateBook { - static readonly type = '[Book] Create Update Book'; - constructor(public payload: CreateUpdateBookDto, public id?: string) {} // <== added id parameter ==> -} -``` - -* We added `id` parameter to the `CreateUpdateBook` action's constructor. - -Open the `book.state.ts` in `app\book\state` folder and replace the `save` method as below: - -```js -@Action(CreateUpdateBook) -save(ctx: StateContext, action: CreateUpdateBook) { - if (action.id) { - return this.bookService.updateByIdAndInput(action.payload, action.id); - } else { - return this.bookService.createByInput(action.payload); - } -} -``` - -#### BookListComponent - -Open `book-list.component.ts` in `app\book\book-list` folder and inject `BookService` dependency by adding it to the constructor and add a variable named `selectedBook`. +Open `book-list.component.ts` in `app\book\book-list` folder and add a variable named `selectedBook`. ```js +import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { BookDto, BookType } from '../models'; -import { GetBooks, CreateUpdateBook } from '../state/book.actions'; -import { BookState } from '../state/book.state'; +import { BookService } from '../services'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; -import { BookService } from '../services'; // <== imported BookService ==> @Component({ selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], + providers: [ListService, { provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class BookListComponent implements OnInit { - @Select(BookState.getBooks) - books$: Observable; + book = { items: [], totalCount: 0 } as PagedResultDto; booksType = BookType; @@ -1058,26 +910,24 @@ export class BookListComponent implements OnInit { (bookType) => typeof this.booksType[bookType] === 'number' ); - loading = false; - isModalOpen = false; form: FormGroup; selectedBook = {} as BookDto; // <== declared selectedBook ==> - constructor(private store: Store, private fb: FormBuilder, private bookService: BookService) {} //<== injected BookService ==> + constructor( + public readonly list: ListService, + private bookService: BookService, + private fb: FormBuilder + ) {} ngOnInit() { - this.get(); - } + const bookStreamCreator = (query) => this.bookService.getListByInput(query); - get() { - this.loading = true; - this.store - .dispatch(new GetBooks()) - .pipe(finalize(() => (this.loading = false))) - .subscribe(() => {}); + this.list.hookToQuery(bookStreamCreator).subscribe((response) => { + this.book = response; + }); } // <== this method is replaced ==> @@ -1109,30 +959,31 @@ export class BookListComponent implements OnInit { }); } + // <== this method is replaced ==> save() { if (this.form.invalid) { return; } - //<== added this.selectedBook.id ==> - this.store - .dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id)) - .subscribe(() => { - this.isModalOpen = false; - this.form.reset(); - this.get(); - }); + // <== added request ==> + const request = this.selectedBook.id + ? this.bookService.updateByIdAndInput(this.form.value, this.selectedBook.id) + : this.bookService.createByInput(this.form.value); + + request.subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.list.get(); + }); } } ``` -* We imported `BookService`. * We declared a variable named `selectedBook` as `BookDto`. -* We injected `BookService` to the constructor. `BookService` is being used to retrieve the book data which is being edited. * We added `editBook` method. This method fetches the book with the given `Id` and sets it to `selectedBook` object. * We replaced the `buildForm` method so that it creates the form with the `selectedBook` data. * We replaced the `createBook` method so it sets `selectedBook` to an empty object. -* We added `selectedBook.id` to the constructor of the new `CreateUpdateBook`. +* We replaced the `save` method. #### Add "Actions" dropdown to the table @@ -1140,27 +991,14 @@ Open the `book-list.component.html` in `app\book\book-list` folder and replace ```html
- - - - - {%{{{ "::Actions" | abpLocalization }}}%} - {%{{{ "::Name" | abpLocalization }}}%} - {%{{{ "::Type" | abpLocalization }}}%} - {%{{{ "::PublishDate" | abpLocalization }}}%} - {%{{{ "::Price" | abpLocalization }}}%} - - - - - + + + +
-
- - {%{{{ data.name }}}%} - {%{{{ booksType[data.type] }}}%} - {%{{{ data.publishDate | date }}}%} - {%{{{ data.price }}}%} - -
+
+ + + + + {%{{{ booksType[row.type] }}}%} + + + + + {%{{{ row.publishDate | date }}}%} + + + + + {%{{{ row.price | currency }}}%} + + +
``` -- We added a `th` for the "Actions" column. +- We added a `ngx-datatable-column` for the "Actions" column. - We added `button` with `ngbDropdownToggle` to open actions when clicked the button. - We have used to [NgbDropdown](https://ng-bootstrap.github.io/#/components/dropdown/examples) for the dropdown menu of actions. @@ -1198,7 +1048,7 @@ Open `book-list.component.html` in `app\book\book-list` folder and find the ` -

{%{{{ (selectedBook.id ? 'AbpIdentity::Edit' : '::NewBook' ) | abpLocalization }}}%}

+

{%{{{ (selectedBook.id ? '::Edit' : '::NewBook' ) | abpLocalization }}}%}

``` @@ -1206,81 +1056,9 @@ Open `book-list.component.html` in `app\book\book-list` folder and find the ` -import { BookService } from '../services'; -import { tap } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; -import { BookDto } from '../models'; - -export class BookStateModel { - public book: PagedResultDto; -} - -@State({ - name: 'BookState', - defaults: { book: {} } as BookStateModel, -}) -@Injectable() -export class BookState { - @Selector() - static getBooks(state: BookStateModel) { - return state.book.items || []; - } - - constructor(private bookService: BookService) {} - - @Action(GetBooks) - get(ctx: StateContext) { - return this.bookService.getListByInput().pipe( - tap((booksResponse) => { - ctx.patchState({ - book: booksResponse, - }); - }) - ); - } - - @Action(CreateUpdateBook) - save(ctx: StateContext, action: CreateUpdateBook) { - if (action.id) { - return this.bookService.updateByIdAndInput(action.payload, action.id); - } else { - return this.bookService.createByInput(action.payload); - } - } - - // <== added DeleteBook action listener ==> - @Action(DeleteBook) - delete(ctx: StateContext, action: DeleteBook) { - return this.bookService.deleteById(action.id); - } -} -``` - -- We imported `DeleteBook` . - -- We added `DeleteBook` action listener to the end of the file. - - #### Delete confirmation popup -Open `book-list.component.ts` in`app\book\book-list` folder and inject the `ConfirmationService`. +Open `book-list.component.ts` in `app\book\book-list` folder and inject the `ConfirmationService`. Replace the constructor as below: @@ -1289,11 +1067,11 @@ import { ConfirmationService } from '@abp/ng.theme.shared'; //... constructor( - private store: Store, - private fb: FormBuilder, - private bookService: BookService, - private confirmation: ConfirmationService // <== added this line ==> -) { } + public readonly list: ListService, + private bookService: BookService, + private fb: FormBuilder, + private confirmation: ConfirmationService // <== added this line ==> +) {} ``` * We imported `ConfirmationService`. @@ -1301,28 +1079,24 @@ constructor( See the [Confirmation Popup documentation](https://docs.abp.io/en/abp/latest/UI/Angular/Confirmation-Service) -In the `book-list.component.ts` add a delete method : +In the `book-list.component.ts` add a delete method: ```js -import { GetBooks, CreateUpdateBook, DeleteBook } from '../state/book.actions' ;// <== imported DeleteBook ==> - -import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; //<== imported Confirmation ==> +import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; //<== imported Confirmation namespace ==> //... delete(id: string) { - this.confirmation - .warn('::AreYouSureToDelete', 'AbpAccount::AreYouSure') - .subscribe(status => { - if (status === Confirmation.Status.confirm) { - this.store.dispatch(new DeleteBook(id)).subscribe(() => this.get()); - } - }); + this.confirmation.warn('::AreYouSureToDelete', 'AbpAccount::AreYouSure').subscribe((status) => { + if (status === Confirmation.Status.confirm) { + this.bookService.deleteById(id).subscribe(() => this.list.get()); + } + }); } ``` -The `delete` method shows a confirmation popup and subscribes for the user response. `DeleteBook` action dispatched only if user clicks to the `Yes` button. The confirmation popup looks like below: +The `delete` method shows a confirmation popup and subscribes for the user response. The `deleteById` method of `BookService` called only if user clicks to the `Yes` button. The confirmation popup looks like below: ![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png) @@ -1335,7 +1109,7 @@ Open `book-list.component.html` in `app\book\book-list` folder and modify the `n ```html
-
diff --git a/docs/en/Tutorials/images/bookstore-actions-buttons.png b/docs/en/Tutorials/images/bookstore-actions-buttons.png index e8243fedc7..f5d8f0870b 100644 Binary files a/docs/en/Tutorials/images/bookstore-actions-buttons.png and b/docs/en/Tutorials/images/bookstore-actions-buttons.png differ diff --git a/docs/en/Tutorials/images/bookstore-angular-file-tree.png b/docs/en/Tutorials/images/bookstore-angular-file-tree.png index ffa8dcd7e2..d921f146fc 100644 Binary files a/docs/en/Tutorials/images/bookstore-angular-file-tree.png and b/docs/en/Tutorials/images/bookstore-angular-file-tree.png differ diff --git a/docs/en/Tutorials/images/bookstore-book-list.png b/docs/en/Tutorials/images/bookstore-book-list.png index d402895c9b..ecdb87c737 100644 Binary files a/docs/en/Tutorials/images/bookstore-book-list.png and b/docs/en/Tutorials/images/bookstore-book-list.png differ diff --git a/docs/en/Tutorials/images/bookstore-confirmation-popup.png b/docs/en/Tutorials/images/bookstore-confirmation-popup.png index a80b180f1c..b17e70638d 100644 Binary files a/docs/en/Tutorials/images/bookstore-confirmation-popup.png and b/docs/en/Tutorials/images/bookstore-confirmation-popup.png differ diff --git a/docs/en/Tutorials/images/bookstore-empty-new-book-modal.png b/docs/en/Tutorials/images/bookstore-empty-new-book-modal.png index 2a02802bb9..f4e969ae22 100644 Binary files a/docs/en/Tutorials/images/bookstore-empty-new-book-modal.png and b/docs/en/Tutorials/images/bookstore-empty-new-book-modal.png differ diff --git a/docs/en/UI/Angular/List-Service.md b/docs/en/UI/Angular/List-Service.md index b3c8571866..900606d725 100644 --- a/docs/en/UI/Angular/List-Service.md +++ b/docs/en/UI/Angular/List-Service.md @@ -53,59 +53,41 @@ class BookComponent { } ``` -> Noticed `list` is `public` and `readonly`? That is because we will use `ListService` properties directly in the component's template. That may be considered as an anti-pattern, but it is much quicker to implement. You can always use public component properties instead. +> Noticed `list` is `public` and `readonly`? That is because we will use `ListService` directly in the component's template. That may be considered as an anti-pattern, but it is much quicker to implement. You can always use public component members to expose the `ListService` instance instead. -Place `ListService` properties into the template like this: +Bind `ListService` to ngx-datatable like this: ```html - - - - - - - {%{{{ '::Name' | abpLocalization }}}%} - - - - - - - - {%{{{ data.name }}}%} - - + + ``` + ## Usage with Observables You may use observables in combination with [AsyncPipe](https://angular.io/guide/observables-in-angular#async-pipe) of Angular instead. Here are some possibilities: -```ts +```js book$ = this.list.hookToQuery(query => this.bookService.getListByInput(query)); ``` ```html - - + + ``` @@ -113,7 +95,7 @@ You may use observables in combination with [AsyncPipe](https://angular.io/guide ...or... -```ts +```js @Select(BookState.getBooks) books$: Observable; @@ -128,32 +110,41 @@ You may use observables in combination with [AsyncPipe](https://angular.io/guide ```html - - + + ``` +> We donot recommend using NGXS store for CRUD pages, unless your application needs to share list information between components or use it later on in another page. + + ## How to Refresh Table on Create/Update/Delete `ListService` exposes a `get` method to trigger a request with the current query. So, basically, whenever a create, update, or delete action resolves, you can call `this.list.get();` and it will call hooked stream creator again. -```ts -this.store.dispatch(new DeleteBook(id)).subscribe(this.list.get); +```js + this.bookService.createByInput(form.value) + .subscribe(() => { + this.list.get(); + + // Other subscription logic here + }); ``` ...or... -```ts -this.bookService.createByInput(form.value) - .subscribe(() => { - this.list.get(); - - // Other subscription logic here - }) +```js + this.store.dispatch(new DeleteBook(id)).subscribe(this.list.get); ``` +> We donot recommend using NGXS store for CRUD pages, unless your application needs to share list information between components or use it later on in another page. + + ## How to Implement Server-Side Search in a Table `ListService` exposes a `filter` property that will trigger a request with the current query and the given search string. All you need to do is to bind it to an input element with two-way binding. @@ -163,3 +154,26 @@ this.bookService.createByInput(form.value) ``` + +## Breaking Change with ABP v3.0 + +We had to modify the `ListService` to make it work with `ngx-datatable`. Previously, the minimum value for `page` property was `1` and you could use it like this: + +```html + + +``` + +As of v3.0, with ngx-datatable, the `page` property has to be set as `0` for inital page. Therefore, if you used `ListService` on your tables before and are going to keep `abp-table`, you need to make the following change: + +```html + + +``` + +**Important Note:** The `abp-table` is not removed, but is deprecated and will be removed in the future. Please consider switching to ngx-datatable. diff --git a/docs/en/UI/Angular/Migration-Guide-v3.md b/docs/en/UI/Angular/Migration-Guide-v3.md new file mode 100644 index 0000000000..f241469193 --- /dev/null +++ b/docs/en/UI/Angular/Migration-Guide-v3.md @@ -0,0 +1,370 @@ +# v2.9 to v3.0 Angular UI Migration Guide + +## What Changed in v3.0? + +### Angular 10 + +The new ABP Angular UI is based on Angular 10 and TypeScript 3.9, and we have dropped support for Angular 8. Nevertheless, ABP modules will keep working with Angular 9. Therefore, if your project is Angular 9, you do not need to update to Angular 10. The update is usually very easy though. + +#### What to Do When Migrating? + +Open a terminal at your root folder and run the following command: + +```sh +yarn ng update @angular/cli @angular/core --force +``` + +This will make the following modifications: + +- Update your package.json and install new packages +- Revise tsconfig.json files to create a "Solution Style" configuration +- Rename `browserlist` as `.browserlistrc` + +On the other hand, it would be better if you check which packages to update first with `yarn ng update` command alone. Angular will give you a list of packages to update. + +![Table of packages to update](./images/table-of-packages-to-update.png) + +When Angular reports the packages above, your command would look like this: + +```sh +yarn ng update @angular/cli @angular/core ng-zorro-antd --force +``` + +> If Angular complains about uncommited changes in your repo, you can either commit/stash them or add `--allow-dirty` parameter to the command. + + +### Config Modules + +In ABP v2.x, every lazy loaded module had a config module available via a separate package and module configuration was as follows: + +```ts +import { AccountConfigModule } from '@abp/ng.account.config'; + +@NgModule({ + imports: [ + // other imports + AccountConfigModule.forRoot({ redirectUrl: '/' }), + ], + // providers, declarations, and bootstrap +}) +export class AppModule {} +``` + +...and in app-routing.module.ts... + +```ts +const routes: Routes = [ + // other route configuration + { + path: 'account', + loadChildren: () => import( + './lazy-libs/account-wrapper.module' + ).then(m => m.AccountWrapperModule), + }, +]; +``` + +Although working, this had a few disadvantages: + +- Every module came in two independent packages, but in reality, those packages were interdependent. +- Configuring lazy loaded modules required a wrapper module. +- ABP Commercial had extensibility system and configuring extensible modules at the root module was increasing the bundle size. + +In ABP v3.0, we have introduced a secondary entry points for each config module as well as a new way to configure lazy loaded modules without the wrappers. Now, the module configuration looks like this: + +```ts +import { AccountConfigModule } from '@abp/ng.account/config'; + +@NgModule({ + imports: [ + // other imports + AccountConfigModule.forRoot(), + ], + // providers, declarations, and bootstrap +}) +export class AppModule {} +``` + +...and in app-routing.module.ts... + +```ts +const routes: Routes = [ + // other route configuration + { + path: 'account', + loadChildren: () => import('@abp/ng.account') + .then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), + }, +]; +``` + +This change helped us reduce bundle size and build times substantially. We believe you will notice the difference in your apps. + +#### A Better Example + +AppModule: + +```ts +import { AccountConfigModule } from '@abp/ng.account/config'; +import { CoreModule } from '@abp/ng.core'; +import { IdentityConfigModule } from '@abp/ng.identity/config'; +import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; +import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; +import { ThemeBasicModule } from '@abp/ng.theme.basic'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NgxsModule } from '@ngxs/store'; +import { environment } from '../environments/environment'; +import { AppRoutingModule } from './app-routing.module'; + +@NgModule({ + imports: [ + BrowserModule, + BrowserAnimationsModule, + AppRoutingModule, + CoreModule.forRoot({ + environment, + sendNullsAsQueryParam: false, + skipGetAppConfiguration: false, + }), + ThemeSharedModule.forRoot(), + AccountConfigModule.forRoot(), + IdentityConfigModule.forRoot(), + TenantManagementConfigModule.forRoot(), + SettingManagementConfigModule.forRoot(), + ThemeBasicModule.forRoot(), + NgxsModule.forRoot(), + ], + // providers, declarations, and bootstrap +}) +export class AppModule {} +``` + +AppRoutingModule: + +```ts +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()), + }, + ], + }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], +}) +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. + +#### What to Do When Migrating? + +- Remove config packages from your project using `yarn remove`. +- Import config modules from secondary entry points (e.g. `@abp/ng.identity/config`). +- Call static `forRoot` method of all new config modules, even if a configuration is not passed. +- 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. + + +### RoutesService + +In ABP v2.x, adding a route to the menu was done by one of two ways: + +- [Via `routes` Property in `AppRoutingModule`](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#via-routes-property-in-approutingmodule) +- [Via ConfigState](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#via-configstate) + +As of v3.0, we have changed how routes are added and modified. We are no longer storing routes in `ConfigState` (breaking change). Instead, there is a new service called `RoutesService` which is used for adding, patching, or removing menu items. Please check [the documentation](./Modifying-the-Menu.md) for details. + +#### What to Do When Migrating? + +- Check if you have ever used `ConfigState` or `ConfigStateService` to add any routes. If so, replace them with `add` method of `RoutesService`. +- Check if you have ever patched a route. If so, convert them to `patch` method of `RoutesService`. +- Double-check that you are using absolute paths and providing a `parentName` instead of `children` property for sub-menu items in `add` or `patch` method calls. + + +### NavItemsService + +In ABP v2.x, adding a navigation element was done [via LayoutStateService](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#how-to-add-an-element-to-right-part-of-the-menu) + +As of v3.0, we have changed how navigation items are added and modified and previous method of doing so is no longer available (breaking change). Please check [the documentation](./Modifying-the-Menu.md) for details. + +#### What to Do When Migrating? + +- Replace all `dispatchAddNavigationElement` calls with `addItems` method of `NavItemsService`. + + +### ngx-datatable + +Until v3, we had been using a custom component, `abp-table`, as the default table. However, data grids are complicated components and implementing a fully-featured one requires considerable effort, which we are planning to put in to other features and issues. + +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: + +```json + { + "input": "node_modules/@swimlane/ngx-datatable/index.css", + "inject": true, + "bundleName": "ngx-datatable-index" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/assets/icons.css", + "inject": true, + "bundleName": "ngx-datatable-icons" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/themes/material.css", + "inject": true, + "bundleName": "ngx-datatable-material" + } +``` + +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`. + +The usage of those directives is rather simple: + +```ts +@Component({ + providers: [ListService], +}) +export class SomeComponent { + data$ = this.list.hookToQuery( + query => this.dataService.get(query) + ); + + constructor( + public readonly list: ListService, + public readonly dataService: SomeDataService, + ) {} +} +``` + +...and in component template... + +```html + + + +``` + +Once you bind the injected `ListService` instance through `NgxDatatableListDirective`, you no longer need to worry about pagination or sorting. Similarly, `NgxDatatableDefaultDirective` gets rid of several property bindings to make ngx-datatable fit our styles. + +#### A Better Example + +```html + + + + + + + + + + + + + + +
{%{{{ row.someOtherProp }}}%}
+
+
+
+``` + +#### What to Do When Migrating? + +- Install `@swimlane/ngx-datatable` package. +- Add ngx-datatable styles in the angular.json file. +- If you can, update your modules according to the example above. +- If you have to do that later and are planning to keep abp-table for a while, make sure you update your pagination according to the [breaking change described here](./List-Service.md). + +**Important Note:** The `abp-table` is not removed, but is deprecated and will be removed in the future. Please consider switching to ngx-datatable. + + +### Deprecated Interfaces + +Some interfaces have long been marked as deprecated and now they are removed. + +#### What to Do When Migrating? + +- Please check if you are still using [anything listed in this issue](https://github.com/abpframework/abp/issues/4281) + + +## What's Next? + +* [Service Proxies](./Service-Proxies.md) + diff --git a/docs/en/UI/Angular/Modifying-the-Menu.md b/docs/en/UI/Angular/Modifying-the-Menu.md index 450a5aecd1..76298e1674 100644 --- a/docs/en/UI/Angular/Modifying-the-Menu.md +++ b/docs/en/UI/Angular/Modifying-the-Menu.md @@ -25,9 +25,99 @@ export const environment = { ## How to Add a Navigation Element +### Via `RoutesService` + +You can add routes to the menu by calling the `add` method of `RoutesService`. It is a singleton service, i.e. provided in root, so you can inject and use it immediately. + +```js +import { RoutesService, eLayoutType } from '@abp/ng.core'; +import { Component } from '@angular/core'; + +@Component(/* component metadata */) +export class AppComponent { + constructor(routes: RoutesService) { + routes.add([ + { + path: '/your-path', + name: 'Your navigation', + order: 101, + iconClass: 'fas fa-question-circle', + requiredPolicy: 'permission key here', + layout: eLayoutType.application, + }, + { + path: '/your-path/child', + name: 'Your child navigation', + parentName: 'Your navigation', + order: 1, + requiredPolicy: 'permission key here', + }, + ]); + } +} +``` + +An alternative and probably cleaner way is to use a route provider. First create a provider: + +```js +// route.provider.ts +import { RoutesService, eLayoutType } from '@abp/ng.core'; +import { APP_INITIALIZER } from '@angular/core'; + +export const APP_ROUTE_PROVIDER = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/your-path', + name: 'Your navigation', + requiredPolicy: 'permission key here', + order: 101, + iconClass: 'fas fa-question-circle', + layout: eLayoutType.application, + }, + { + path: '/your-path/child', + name: 'Your child navigation', + parentName: 'Your navigation', + requiredPolicy: 'permission key here', + order: 1, + }, + ]); + }; +} +``` + +...and then in app.module.ts... + +```js +import { NgModule } from '@angular/core'; +import { APP_ROUTE_PROVIDER } from './route.provider'; + +@NgModule({ + providers: [APP_ROUTE_PROVIDER], + // imports, declarations, and bootstrap +}) +export class AppModule {} +``` + +Here is what every property works as: + +- `path` is the absolute path of the navigation element. +- `name` is the label of the navigation element. A localization key or a localization object can be passed. +- `parentName` is a reference to the `name` of the parent route in the menu and is used for creating multi-level menu items. +- `requiredPolicy` is the permission key to access the page. See the [Permission Management document](./Permission-Management.md) +- `order` is the order of the navigation element. "Administration" has an order of `100`, so keep that in mind when ordering top level menu items. +- `iconClass` is the class of the `i` tag, which is placed to the left of the navigation label. +- `layout` defines in which layout the route will be loaded. (default: `eLayoutType.empty`) +- `invisible` makes the item invisible in the menu. (default: `false`) + ### Via `routes` Property in `AppRoutingModule` -You can define your routes by adding `routes` as a child property to `data` property of a route configuration in the `app-routing.module`. The `@abp/ng.core` package organizes your routes and stores them in the `ConfigState`. `ApplicationLayoutComponent` gets routes from store and displays them on the menu. +You can define your routes by adding `routes` as a child property to `data` property of a route configuration in the `app-routing.module`. The `@abp/ng.core` package organizes your routes and stores them in the `RoutesService`. You can add the `routes` property like below: @@ -37,7 +127,7 @@ You can add the `routes` property like below: data: { routes: { name: 'Your navigation', - order: 3, + order: 101, iconClass: 'fas fa-question-circle', requiredPolicy: 'permission key here', children: [ @@ -48,99 +138,77 @@ You can add the `routes` property like below: requiredPolicy: 'permission key here', }, ], - } as ABP.Route, // can be imported from @abp/ng.core - } + }, + }, } ``` -- `name` is the label of the navigation element. A localization key or a localization object can be passed. -- `order` is the order of the navigation element. -- `iconClass` is the class of the `i` tag, which is placed to the left of the navigation label. -- `requiredPolicy` is the permission key to access the page. See the [Permission Management document](./Permission-Management.md) -- `children` is an array and is used for declaring child navigation elements. The child navigation element will be placed as a child route which will be available at `'/your-path/child'` based on the given `path` property. - -After adding the `routes` property as described above, the navigation menu looks like this: - -![navigation-menu-via-app-routing](./images/navigation-menu-via-app-routing.png) - -## Via ConfigState - -The `dispatchAddRoute` method of `ConfigStateService` adds a new navigation element to the menu. +Alternatively, you can do this: ```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', -} as Omit; - -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 -// this.config is instance of ConfigStateService -// eIdentityRouteNames enum can be imported from @abp/ng.identity - -const newRoute: ABP.Route = { - parentName: eIdentityRouteNames.IdentityManagement, - name: 'My New Page', - iconClass: 'fa fa-dashboard', - path: 'page', - invisible: false, - order: 3, - requiredPolicy: 'MyProjectName.MyNewPage' -} as Omit; - -this.config.dispatchAddRoute(newRoute); -// returns a state stream which emits after dispatch action is complete +{ + path: 'your-path', + data: { + routes: [ + { + path: '/your-path', + name: 'Your navigation', + order: 101, + iconClass: 'fas fa-question-circle', + requiredPolicy: 'permission key here', + }, + { + path: '/your-path/child', + name: 'Your child navigation', + parentName: 'Your navigation', + order: 1, + requiredPolicy: 'permission key here', + }, + ] as ABP.Route[], // can be imported from @abp/ng.core + }, +} ``` -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'`. +The advantage of the second method is that you are not bound to the parent/child structure and use any paths you like. -The new route will be added like below: +After adding the `routes` property as described above, the navigation menu looks like this: -![navigation-menu-via-config-state](./images/navigation-menu-via-config-state.png) +![navigation-menu-via-app-routing](./images/navigation-menu-via-app-routing.png) -## How to Patch a Navigation Element +## How to Patch or Remove a Navigation Element -The `dispatchPatchRouteByName` method finds a route by its name and replaces its configuration in the store with the new configuration passed as the second parameter. +The `patch` method of `RoutesService` finds a route by its name and replaces its configuration with the new configuration passed as the second parameter. Similarly, `remove` method finds a route and removes it along with its children. ```js -// this.config is instance of ConfigStateService -// eIdentityRouteNames enum can be imported from @abp/ng.identity +// this.routes is instance of RoutesService +// eThemeSharedRouteNames enum can be imported from @abp/ng.theme.shared + +const dashboardRouteConfig: ABP.Route = { + path: '/dashboard', + name: '::Menu:Dashboard', + parentName: '::Menu:Home', + order: 1, + layout: eLayoutType.application, +}; -const newRouteConfig: Partial = { +const newHomeRouteConfig: Partial = { iconClass: 'fas fa-home', - parentName: eIdentityRouteNames.Administration, + parentName: eThemeSharedRouteNames.Administration, order: 0, - children: [ - { - name: 'Dashboard', - path: 'dashboard', - }, - ], }; -this.config.dispatchPatchRouteByName('::Menu:Home', newRouteConfig); -// returns a state stream which emits after dispatch action is complete +this.routes.add([dashboardRouteConfig]); +this.routes.patch('::Menu:Home', newHomeRouteConfig); +this.routes.remove(['Your navigation']); ``` -* Moved the _Home_ navigation under the _Administration_ dropdown based on given `parentName`. -* Added an icon. -* Specified the order. -* Added a child route named _Dashboard_. +- Moved the _Home_ navigation under the _Administration_ dropdown based on given `parentName`. +- Added an icon to _Home_. +- Specified the order and made _Home_ the first item in list. +- Added a route named _Dashboard_ as a child of _Home_. +- Removed _Your navigation_ along with its child route. -After the patch above, navigation elements looks like below: +After the operations above, the new menu looks like below: ![navigation-menu-after-patching](./images/navigation-menu-after-patching.png) diff --git a/docs/en/UI/Angular/images/table-of-packages-to-update.png b/docs/en/UI/Angular/images/table-of-packages-to-update.png new file mode 100644 index 0000000000..fc492d3740 Binary files /dev/null and b/docs/en/UI/Angular/images/table-of-packages-to-update.png differ diff --git a/docs/en/Unit-Of-Work.md b/docs/en/Unit-Of-Work.md index 907e601d30..743aa515bb 100644 --- a/docs/en/Unit-Of-Work.md +++ b/docs/en/Unit-Of-Work.md @@ -325,7 +325,7 @@ public async Task CreateAsync(string name) If your intent is just to save the changes after creating/updating/deleting an entity, it is suggested to use the `autoSave` option instead of manually using the `CurrentUnitOfWork.SaveChangesAsync()`. -> **Note-1**: All changes are automatically saved when a unit of work ends without any error. So, don't call `SaveChangesAsync()` unless you really need it. +> **Note-1**: All changes are automatically saved when a unit of work ends without any error. So, don't call `SaveChangesAsync()` and don't set `autoSave` to `true` unless you really need it. > > **Note-2**: If you use `Guid` as the primary key, you never need to save changes on insert to just get the generated id, because `Guid` keys are set in the application and are immediately available once you create a new entity. diff --git a/docs/en/Virtual-File-System.md b/docs/en/Virtual-File-System.md index c5a154bd3c..a519b02ec6 100644 --- a/docs/en/Virtual-File-System.md +++ b/docs/en/Virtual-File-System.md @@ -1,40 +1,30 @@ -## Virtual File System +# Virtual File System The Virtual File System makes it possible to manage files that do not physically exist on the file system (disk). It's mainly used to embed (js, css, image..) files into assemblies and use them like physical files at runtime. -### Volo.Abp.VirtualFileSystem Package +## Installation -Volo.Abp.VirtualFileSystem is the core package of the virtual file system. Install it in your project using the package manager console (PMC): +> Most of the times you don't need to manually install this package since it comes pre-installed with the [application startup template](Startup-Templates/Application.md). -``` -Install-Package Volo.Abp.VirtualFileSystem -``` +[Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) is the main page of the Virtual File System. -> This package is already installed by default with the startup template. So, most of the time, you will not need to install it manually. +Use the ABP CLI to add this package to your project: -Then you can add **AbpVirtualFileSystemModule** dependency to your module: +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI), if you haven't installed it. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.VirtualFileSystem` package. +* Run `abp add-package Volo.Abp.VirtualFileSystem` command. -```c# -using Volo.Abp.Modularity; -using Volo.Abp.VirtualFileSystem; +If you want to do it manually, install the [Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) NuGet package to your project and add `[DependsOn(typeof(AbpVirtualFileSystemModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. -namespace MyCompany.MyProject -{ - [DependsOn(typeof(AbpVirtualFileSystemModule))] - public class MyModule : AbpModule - { - //... - } -} -``` +## Working with the Embedded Files -#### Registering Embedded Files +### Embed the Files -A file should be first marked as an embedded resource to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example: +A file should be first marked as an **embedded resource** to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example: ![build-action-embedded-resource-sample](images/build-action-embedded-resource-sample.png) -If you want to add multiple files, this can be tedious. Alternatively, you can directly edit your **.csproj** file: +If you want to add multiple files, this can be tedious. Alternatively, you can directly edit your `.csproj` file: ````C# @@ -45,76 +35,60 @@ If you want to add multiple files, this can be tedious. Alternatively, you can d This configuration recursively adds all files under the **MyResources** folder of the project (including the files you will add in the future). -Then the module needs to be configured using `AbpVirtualFileSystemOptions` to register the embedded files to the virtual file system. Example: +Embedding a file in the project/assembly may cause problems if a file name contains some special chars. To overcome this limitation; -````C# -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; -using Volo.Abp.VirtualFileSystem; +1. Add [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet package to the project that contains the embedded resource(s). +2. Add `true` into the `...` section of your `.csproj` file. -namespace MyCompany.MyProject -{ - [DependsOn(typeof(AbpVirtualFileSystemModule))] - public class MyModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - //Register all embedded files of this assembly to the virtual file system - options.FileSets.AddEmbedded("YourRootNameSpace"); - }); +> While these two steps are optional and ABP can work without these configuration, it is strongly suggested to make it. - //... - } - } -} -```` +### Configure the AbpVirtualFileSystemOptions -The `AddEmbedded` extension method takes a class, finds all embedded files from the assembly of the given class and registers them to the virtual file system. More concisely it could be written as follows: +Use `AbpVirtualFileSystemOptions` [options class](Options.md) to register the embedded files to the virtual file system in the `ConfigureServices` method of your [module](Module-Development-Basics.md). -````C# -options.FileSets.Add( - new EmbeddedFileSet(typeof(MyModule).Assembly), "YourRootNameSpace"); +**Example: Add embedded files to the virtual file system** + +````csharp +Configure(options => +{ + options.FileSets.AddEmbedded(); +}); ```` -> "YourRootNameSpace" is the root namespace of your project. It can be empty if your root namespace is empty. +The `AddEmbedded` extension method takes a class, finds all embedded files from the **assembly of the given class** and registers them to the virtual file system. It is common to pass the module class as the generic argument. -#### Getting Virtual Files: IVirtualFileProvider +`AddEmbedded` can get two optional parameters; -After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get files or directory contents: +* `baseNamespace`: This may only needed if you didn't configure the `GenerateEmbeddedFilesManifest` step explained above and your root namespace is not empty. In this case, set your root namespace here. +* `baseFolder`: If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), then you can set the base folder relative to your project root page. -````C# -public class MyService +**Example: Add files under the `MyFiles` folder in the project** + +````csharp +Configure(options => { - private readonly IVirtualFileProvider _virtualFileProvider; + options.FileSets.AddEmbedded( + baseNamespace: "Acme.BookStore.MyFiles", + baseFolder: "/MyFiles" + ); +}); +```` - public MyService(IVirtualFileProvider virtualFileProvider) - { - _virtualFileProvider = virtualFileProvider; - } +This example assumes; - public void Foo() - { - //Getting a single file - var file = _virtualFileProvider.GetFileInfo("/MyResources/js/test.js"); - var fileContent = file.ReadAsString(); //ReadAsString is an extension method of ABP +* Your project root (default) namespace is `Acme.BookStore`. +* Your project has a folder, named `MyFiles` +* You only want to add `MyFiles` folder to the virtual file system. - //Getting all files/directories under a directory - var directoryContents = _virtualFileProvider.GetDirectoryContents("/MyResources/js"); - } -} -```` +### Dealing With Embedded Files During Development -#### Dealing With Embedded Files During Development - -Embedding a file into a module assembly and being able to use it from another project just by referencing the assembly (or adding a nuget package) is invaluable for creating a re-usable module. However, it does make it a little bit harder to develop the module itself. +Embedding a file into an assembly and being able to use it from another project just by referencing the assembly (or adding a NuGet package) is invaluable for creating a re-usable module. However, it makes it a little bit harder to develop the module itself. Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious. What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible. -The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time. +The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time. ````C# [DependsOn(typeof(MyModule))] @@ -128,29 +102,63 @@ public class MyWebAppModule : AbpModule { Configure(options => { - //ReplaceEmbeddedByPhysical gets the root folder of the MyModule project options.FileSets.ReplaceEmbeddedByPhysical( - Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}MyModuleProject", Path.DirectorySeparatorChar)) + Path.Combine( + hostingEnvironment.ContentRootPath, + string.Format( + "..{0}MyModuleProject", + Path.DirectorySeparatorChar + ) + ) ); }); } - - //... } } ```` The code above assumes that `MyWebAppModule` and `MyModule` are two different projects in a Visual Studio solution and `MyWebAppModule` depends on the `MyModule`. -### ASP.NET Core Integration +> The [application startup template](Startup-Templates/Application.md) already uses this technique for the localization files. So, when you change a localization file it automatically detects the change. + +## IVirtualFileProvider + +After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get files or directory contents: + +````C# +public class MyService +{ + private readonly IVirtualFileProvider _virtualFileProvider; + + public MyService(IVirtualFileProvider virtualFileProvider) + { + _virtualFileProvider = virtualFileProvider; + } + + public void Foo() + { + //Getting a single file + var file = _virtualFileProvider + .GetFileInfo("/MyResources/js/test.js"); + + var fileContent = file.ReadAsString(); + + //Getting all files/directories under a directory + var directoryContents = _virtualFileProvider + .GetDirectoryContents("/MyResources/js"); + } +} +```` + +## ASP.NET Core Integration The Virtual File System is well integrated to ASP.NET Core: * Virtual files can be used just like physical (static) files in a web application. * Js, css, image files and all other web content types can be embedded into assemblies and used just like the physical files. -* An application (or another module) can override a virtual file of a module just like placing a file with the same name and extension into the same folder of the virtual file. +* An application (or another module) can **override a virtual file** of a module just like placing a file with the same name and extension into the same folder of the virtual file. -#### Virtual Files Middleware +### UseVirtualFiles Middleware The Virtual Files Middleware is used to serve embedded (js, css, image...) files to clients/browsers just like physical files in the **wwwroot** folder. Add it just after the static file middleware as shown below: @@ -160,4 +168,14 @@ app.UseVirtualFiles(); Adding virtual files middleware after the static files middleware makes it possible to override a virtual file with a real physical file simply by placing it in the same location as the virtual file. ->The Virtual File Middleware only serves the virtual wwwroot folder contents - just like the other static files. +> `UseVirtualFiles()` is already configured for the [application startup template](Startup-Templates/Application.md). + +#### Static Virtual File Folders + +By default, ASP.NET Core only allows the `wwwroot` folder to contain the static files consumed by the clients. When you use the `UseVirtualFiles` middleware, the following folders also can contain static files: + +* Pages +* Views +* Themes + +This allows to add `.js`, `.css`... files near to your `.cshtml` file that is easier to develop and maintain your project. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index afcffe50a5..4a29b053b9 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -366,6 +366,10 @@ { "text": "Angular", "items": [ + { + "text": "Migration Guide v2.x to v3", + "path": "UI/Angular/Migration-Guide-v3.md" + }, { "text": "Service Proxies", "path": "UI/Angular/Service-Proxies.md" diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index e02d40b1e0..a3ee894572 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -305,6 +305,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -907,6 +913,18 @@ Global {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Debug|Any CPU.Build.0 = Debug|Any CPU {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.ActiveCfg = Release|Any CPU {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.Build.0 = Release|Any CPU + {1738845A-5348-4EB8-B736-CD1D22A808B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1738845A-5348-4EB8-B736-CD1D22A808B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1738845A-5348-4EB8-B736-CD1D22A808B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1738845A-5348-4EB8-B736-CD1D22A808B4}.Release|Any CPU.Build.0 = Release|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}.Release|Any CPU.Build.0 = Release|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1061,6 +1079,9 @@ Global {75E5C841-5F36-4C44-A532-57CB8E7FFE15} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {C44242F7-D55D-4867-AAF4-A786E404312E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {A80E9A0B-8932-4B5D-83FB-6751708FD484} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {1738845A-5348-4EB8-B736-CD1D22A808B4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {60D0E384-965E-4F81-9D71-B28F419254FC} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj index 544099c653..af317c263c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json deleted file mode 100644 index fcbf86698d..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53450/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Authentication.OAuth": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53451/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json deleted file mode 100644 index 67c494aa28..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53760/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.MultiTenancy": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53763/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json deleted file mode 100644 index e17b14d87f..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52302/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52303/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs index 2197d7b08e..ce08bebe87 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs @@ -14,5 +14,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public string UserName { get; set; } public string Email { get; set; } + + public string[] Roles { get; set; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json deleted file mode 100644 index c2dcf8fae6..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61183/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json deleted file mode 100644 index 7bcd0a8aff..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53762/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Bundling": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53765/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json deleted file mode 100644 index 6870af4120..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56918/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.TenantSwitch": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56919/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json deleted file mode 100644 index 4ae9c1d2f1..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53766/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Packages": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53768/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs new file mode 100644 index 0000000000..691f377c6c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyScriptContributor.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy +{ + public class UppyScriptContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/uppy/uppy.min.js"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs new file mode 100644 index 0000000000..c10509515d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Uppy/UppyStyleContributor.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy +{ + public class UppyStyleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/uppy/uppy.min.css"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json deleted file mode 100644 index 763367a2a8..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50398/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50399/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj index c390cfe58c..206224f918 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj @@ -22,8 +22,6 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj index 078563ace3..8c098c3300 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj @@ -18,8 +18,6 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs index d14c92c62e..a21a3c51cd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml index d7a2e893d4..65853b9d73 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml @@ -1,13 +1,17 @@ @using Localization.Resources.AbpUi @using Microsoft.Extensions.Localization + @inject IStringLocalizer L +
- +
- +
-
\ No newline at end of file + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/_ViewImports.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/_ViewImports.cshtml new file mode 100644 index 0000000000..c1da1f5f10 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/_ViewImports.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json deleted file mode 100644 index f928a8ea06..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51544/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:51545/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj index 71e1257a4d..4b0fbc24cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj @@ -23,9 +23,7 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json deleted file mode 100644 index e9779c3c37..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53761/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI.Widgets": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53764/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json deleted file mode 100644 index 3b3fd9ea46..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55913/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc.UI": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj index d14d639fbb..99a9357392 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json deleted file mode 100644 index 3c84fd21f2..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53767/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Mvc": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53769/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index 90500a456b..47899625d4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -26,9 +26,9 @@ - - - + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 263178e0ed..e3cee4e72b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -16,6 +16,7 @@ using System.Net; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.DataAnnotations; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; @@ -25,6 +26,7 @@ using Microsoft.Extensions.Localization; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.AspNetCore.Mvc.Conventions; +using Volo.Abp.AspNetCore.Mvc.DataAnnotations; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; @@ -164,6 +166,9 @@ namespace Volo.Abp.AspNetCore.Mvc partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application)); partManager.ApplicationParts.AddIfNotContains(typeof(AbpAspNetCoreMvcModule).Assembly); + context.Services.Replace(ServiceDescriptor.Singleton()); + context.Services.AddSingleton(); + Configure(mvcOptions => { mvcOptions.AddAbp(context.Services); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 4aa183a43d..59166d3e1d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -5,7 +5,6 @@ using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.Features; using Volo.Abp.AspNetCore.Mvc.ModelBinding; -using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata; using Volo.Abp.AspNetCore.Mvc.Response; using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Validation; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index 5aa8f07059..54815d6ff5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -117,7 +117,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations Id = _currentUser.Id, TenantId = _currentUser.TenantId, UserName = _currentUser.UserName, - Email = _currentUser.Email + Email = _currentUser.Email, + Roles = _currentUser.Roles }; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs index 15854e10a6..781866232b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs @@ -52,6 +52,21 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication return new SignOutResult(ChallengeAuthenticationSchemas); } + [HttpGet] + public async Task FrontChannelLogout(string sid) + { + if (User.Identity.IsAuthenticated) + { + var currentSid = User.FindFirst("sid").Value ?? string.Empty; + if (string.Equals(currentSid, sid, StringComparison.Ordinal)) + { + await Logout(); + } + } + + return NoContent(); + } + protected RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null) { return Redirect(GetRedirectUrl(returnUrl, returnUrlHash)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs new file mode 100644 index 0000000000..c9b7cb21b3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class AbpValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider + { + private readonly ValidationAttributeAdapterProvider _defaultAdapter; + + public AbpValidationAttributeAdapterProvider(ValidationAttributeAdapterProvider defaultAdapter) + { + _defaultAdapter = defaultAdapter; + } + + public virtual IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer) + { + var type = attribute.GetType(); + + if (type == typeof(DynamicStringLengthAttribute)) + { + return new DynamicStringLengthAttributeAdapter((DynamicStringLengthAttribute) attribute, stringLocalizer); + } + + if (type == typeof(DynamicMaxLengthAttribute)) + { + return new DynamicMaxLengthAttributeAdapter((DynamicMaxLengthAttribute) attribute, stringLocalizer); + } + + return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs new file mode 100644 index 0000000000..f8193a2e79 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs @@ -0,0 +1,46 @@ +using System.Globalization; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class DynamicMaxLengthAttributeAdapter : AttributeAdapterBase + { + private readonly string _max; + + public DynamicMaxLengthAttributeAdapter( + DynamicMaxLengthAttribute attribute, + IStringLocalizer stringLocalizer) + : base(attribute, stringLocalizer) + { + _max = Attribute.Length.ToString(CultureInfo.InvariantCulture); + } + + public override string GetErrorMessage(ModelValidationContextBase validationContext) + { + Check.NotNull(validationContext, nameof(validationContext)); + + return GetErrorMessage( + validationContext.ModelMetadata, + validationContext.ModelMetadata.GetDisplayName(), + Attribute.Length + ); + } + + public override void AddValidation(ClientModelValidationContext context) + { + Check.NotNull(context, nameof(context)); + + MergeAttribute(context.Attributes, "data-val", "true"); + MergeAttribute(context.Attributes, "data-val-maxlength", GetErrorMessage(context)); + MergeAttribute(context.Attributes, "data-val-maxlength-max", _max); + + if (Attribute.Length != int.MaxValue) + { + MergeAttribute(context.Attributes, "data-val-maxlength-max", _max); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs new file mode 100644 index 0000000000..d4c46b85f3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicStringLengthAttributeAdapter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class DynamicStringLengthAttributeAdapter : AttributeAdapterBase + { + private readonly string _max; + private readonly string _min; + + public DynamicStringLengthAttributeAdapter( + DynamicStringLengthAttribute attribute, + IStringLocalizer stringLocalizer) + : base(attribute, stringLocalizer) + { + _max = Attribute.MaximumLength.ToString(CultureInfo.InvariantCulture); + _min = Attribute.MinimumLength.ToString(CultureInfo.InvariantCulture); + } + + public override void AddValidation(ClientModelValidationContext context) + { + Check.NotNull(context, nameof(context)); + + MergeAttribute(context.Attributes, "data-val", "true"); + MergeAttribute(context.Attributes, "data-val-length", GetErrorMessage(context)); + + if (Attribute.MaximumLength != int.MaxValue) + { + MergeAttribute(context.Attributes, "data-val-length-max", _max); + } + + if (Attribute.MinimumLength != 0) + { + MergeAttribute(context.Attributes, "data-val-length-min", _min); + } + } + + public override string GetErrorMessage(ModelValidationContextBase validationContext) + { + Check.NotNull(validationContext, nameof(validationContext)); + + return GetErrorMessage( + validationContext.ModelMetadata, + validationContext.ModelMetadata.GetDisplayName(), + Attribute.MaximumLength, + Attribute.MinimumLength + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json deleted file mode 100644 index cb1bd6546c..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Serilog/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61851/", - "sslPort": 44301 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.Serilog": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj index b0bb8930df..348aeb0342 100644 --- a/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Serilog/Volo.Abp.AspNetCore.Serilog.csproj @@ -22,7 +22,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json deleted file mode 100644 index 418c7f2621..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53900/", - "sslPort": 44362 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.SignalR": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json deleted file mode 100644 index fca5708e7b..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.TestBase/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53783/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore.TestBase": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53784/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj b/framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj index 2ff1d539a6..a4dcd1c8c5 100644 --- a/framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj +++ b/framework/src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj @@ -24,7 +24,7 @@
- + diff --git a/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json deleted file mode 100644 index 4098e788cd..0000000000 --- a/framework/src/Volo.Abp.AspNetCore/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53374/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.AspNetCore": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53375/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj b/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj index e5183688df..b8ff62572b 100644 --- a/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj +++ b/framework/src/Volo.Abp.Authorization/Volo.Abp.Authorization.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj index 45cc6e2d94..3c5c275117 100644 --- a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj +++ b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs index 7823502013..9cfc748c4e 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Volo.Abp.Modularity; using Volo.Abp.Quartz; @@ -18,6 +19,11 @@ namespace Volo.Abp.BackgroundWorkers.Quartz context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar()); } + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddSingleton(typeof(QuartzPeriodicBackgroundWorkerAdapter<>)); + } + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) { var options = context.ServiceProvider.GetService>().Value; diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorkerAdapter.cs new file mode 100644 index 0000000000..dd6a3019b2 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorkerAdapter.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BackgroundWorkers.Quartz +{ + public interface IQuartzBackgroundWorkerAdapter : IQuartzBackgroundWorker + { + void BuildWorker(IBackgroundWorker worker); + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs new file mode 100644 index 0000000000..96cf3988bd --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs @@ -0,0 +1,88 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using Quartz; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers.Quartz +{ + public class QuartzPeriodicBackgroundWorkerAdapter : QuartzBackgroundWorkerBase, + IQuartzBackgroundWorkerAdapter + where TWorker : IBackgroundWorker + { + private readonly MethodInfo _doWorkAsyncMethod; + private readonly MethodInfo _doWorkMethod; + + public QuartzPeriodicBackgroundWorkerAdapter() + { + AutoRegister = false; + + _doWorkAsyncMethod = typeof(TWorker).GetMethod("DoWorkAsync", BindingFlags.Instance | BindingFlags.NonPublic); + _doWorkMethod = typeof(TWorker).GetMethod("DoWork", BindingFlags.Instance | BindingFlags.NonPublic); + + } + + public void BuildWorker(IBackgroundWorker worker) + { + int? period; + var workerType = worker.GetType(); + + if (worker is AsyncPeriodicBackgroundWorkerBase || worker is PeriodicBackgroundWorkerBase) + { + if (typeof(TWorker) != worker.GetType()) + { + throw new ArgumentException($"{nameof(worker)} type is different from the generic type"); + } + + var timer = (AbpTimer) worker.GetType().GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker); + period = timer?.Period; + } + else + { + return; + } + + if (period == null) + { + return; + } + + JobDetail = JobBuilder + .Create>() + .WithIdentity(workerType.FullName) + .Build(); + Trigger = TriggerBuilder.Create() + .WithIdentity(workerType.FullName) + .WithSimpleSchedule(builder => builder.WithInterval(TimeSpan.FromMilliseconds(period.Value)).RepeatForever()) + .Build(); + } + + public override async Task Execute(IJobExecutionContext context) + { + var worker = (IBackgroundWorker) ServiceProvider.GetService(typeof(TWorker)); + var workerContext = new PeriodicBackgroundWorkerContext(ServiceProvider); + + switch (worker) + { + case AsyncPeriodicBackgroundWorkerBase asyncWorker: + { + if (_doWorkAsyncMethod != null) + { + await (Task) _doWorkAsyncMethod.Invoke(asyncWorker, new object[] {workerContext}); + } + + break; + } + case PeriodicBackgroundWorkerBase syncWorker: + { + if (_doWorkMethod != null) + { + _doWorkMethod.Invoke(syncWorker, new object[] {workerContext}); + } + + break; + } + } + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs index 35359b1d27..d670c1db98 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using Quartz; using Volo.Abp.DependencyInjection; @@ -50,6 +51,19 @@ namespace Volo.Abp.BackgroundWorkers.Quartz await DefaultScheduleJobAsync(quartzWork); } } + else + { + var adapterType = typeof(QuartzPeriodicBackgroundWorkerAdapter<>).MakeGenericType(worker.GetType()); + + var workerAdapter = Activator.CreateInstance(adapterType) as IQuartzBackgroundWorkerAdapter; + + workerAdapter?.BuildWorker(worker); + + if (workerAdapter?.Trigger != null) + { + await DefaultScheduleJobAsync(workerAdapter); + } + } } protected virtual async Task DefaultScheduleJobAsync(IQuartzBackgroundWorker quartzWork) @@ -66,4 +80,4 @@ namespace Volo.Abp.BackgroundWorkers.Quartz } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj b/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj index 1e7c51f4fc..6434e1d43c 100644 --- a/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs index 945c930175..3b3cbc4743 100644 --- a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs @@ -15,6 +15,7 @@ namespace Volo.Abp.BlobStoring.Azure Action azureConfigureAction) { containerConfiguration.ProviderType = typeof(AzureBlobProvider); + containerConfiguration.NamingNormalizers.TryAdd(); azureConfigureAction(new AzureBlobProviderConfiguration(containerConfiguration)); diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs new file mode 100644 index 0000000000..43b479e08e --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobNamingNormalizer.cs @@ -0,0 +1,52 @@ +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class AzureBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency + { + /// + ///https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names + /// + public virtual string NormalizeContainerName(string containerName) + { + // All letters in a container name must be lowercase. + containerName = containerName.ToLower(); + + // Container names can contain only letters, numbers, and the dash (-) character. + containerName = Regex.Replace(containerName, "[^a-z0-9-]", string.Empty); + + // Every dash (-) character must be immediately preceded and followed by a letter or number; + // consecutive dashes are not permitted in container names. + // Container names must start or end with a letter or number + containerName = Regex.Replace(containerName, "-{2,}", "-"); + containerName = Regex.Replace(containerName, "^-", string.Empty); + containerName = Regex.Replace(containerName, "-$", string.Empty); + + // Container names must be from 3 through 63 characters long. + if (containerName.Length < 3) + { + var length = containerName.Length; + for (var i = 0; i < 3 - length; i++) + { + containerName += "0"; + } + } + + if (containerName.Length > 63) + { + containerName = containerName.Substring(0, 63); + } + + return containerName; + } + + /// + ///https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#blob-names + /// + public virtual string NormalizeBlobName(string blobName) + { + return blobName; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj index 88fc08e34e..65242db157 100644 --- a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs index 24357554eb..2bb818da56 100644 --- a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs @@ -9,16 +9,17 @@ namespace Volo.Abp.BlobStoring.FileSystem { return new FileSystemBlobProviderConfiguration(containerConfiguration); } - + public static BlobContainerConfiguration UseFileSystem( this BlobContainerConfiguration containerConfiguration, Action fileSystemConfigureAction) { containerConfiguration.ProviderType = typeof(FileSystemBlobProvider); - + containerConfiguration.NamingNormalizers.TryAdd(); + fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration)); - + return containerConfiguration; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs new file mode 100644 index 0000000000..f7d273dca7 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobNamingNormalizer.cs @@ -0,0 +1,41 @@ +using System; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency + { + private readonly IOSPlatformProvider _iosPlatformProvider; + + public FileSystemBlobNamingNormalizer(IOSPlatformProvider iosPlatformProvider) + { + _iosPlatformProvider = iosPlatformProvider; + } + + public virtual string NormalizeContainerName(string containerName) + { + return Normalize(containerName); + } + + public virtual string NormalizeBlobName(string blobName) + { + return Normalize(blobName); + } + + protected virtual string Normalize(string fileName) + { + var os = _iosPlatformProvider.GetCurrentOSPlatform(); + if (os == OSPlatform.Windows) + { + // A filename cannot contain any of the following characters: \ / : * ? " < > | + // In order to support the directory included in the blob name, remove / and \ + fileName = Regex.Replace(fileName, "[:\\*\\?\"<>\\|]", string.Empty); + } + + return fileName; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs index d165a40316..cd2287e964 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs @@ -1,7 +1,9 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; @@ -84,18 +86,22 @@ namespace Volo.Abp.BlobStoring protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IServiceProvider ServiceProvider { get; } + public BlobContainer( string containerName, BlobContainerConfiguration configuration, IBlobProvider provider, ICurrentTenant currentTenant, - ICancellationTokenProvider cancellationTokenProvider) + ICancellationTokenProvider cancellationTokenProvider, + IServiceProvider serviceProvider) { ContainerName = containerName; Configuration = configuration; Provider = provider; CurrentTenant = currentTenant; CancellationTokenProvider = cancellationTokenProvider; + ServiceProvider = serviceProvider; } public virtual async Task SaveAsync( @@ -106,11 +112,13 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = NormalizeNaming(ContainerName, name); + await Provider.SaveAsync( new BlobProviderSaveArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, stream, overrideExisting, CancellationTokenProvider.FallbackToProvider(cancellationToken) @@ -125,11 +133,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.DeleteAsync( new BlobProviderDeleteArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -142,11 +153,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.ExistsAsync( new BlobProviderExistsArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -158,7 +172,7 @@ namespace Volo.Abp.BlobStoring CancellationToken cancellationToken = default) { var stream = await GetOrNullAsync(name, cancellationToken); - + if (stream == null) { //TODO: Consider to throw some type of "not found" exception and handle on the HTTP status side @@ -175,11 +189,14 @@ namespace Volo.Abp.BlobStoring { using (CurrentTenant.Change(GetTenantIdOrNull())) { + var (normalizedContainerName, normalizedBlobName) = + NormalizeNaming(ContainerName, name); + return await Provider.GetOrNullAsync( new BlobProviderGetArgs( - ContainerName, + normalizedContainerName, Configuration, - name, + normalizedBlobName, CancellationTokenProvider.FallbackToProvider(cancellationToken) ) ); @@ -195,5 +212,28 @@ namespace Volo.Abp.BlobStoring return CurrentTenant.Id; } + + protected virtual (string, string) NormalizeNaming(string containerName, string blobName) + { + if (!Configuration.NamingNormalizers.Any()) + { + return (containerName, blobName); + } + + using (var scope = ServiceProvider.CreateScope()) + { + foreach (var normalizerType in Configuration.NamingNormalizers) + { + var normalizer = scope.ServiceProvider + .GetRequiredService(normalizerType) + .As(); + + containerName = normalizer.NormalizeContainerName(containerName); + blobName = normalizer.NormalizeBlobName(blobName); + } + + return (containerName, blobName); + } + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs index 46f13a1c3d..7ca94b4b42 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using JetBrains.Annotations; +using Volo.Abp.Collections; namespace Volo.Abp.BlobStoring { @@ -18,17 +19,20 @@ namespace Volo.Abp.BlobStoring /// then the container is shared by all tenants in the system. /// /// This can be true even if your application is not multi-tenant. - /// + /// /// Default: true. /// public bool IsMultiTenant { get; set; } = true; + public ITypeList NamingNormalizers { get; } + [NotNull] private readonly Dictionary _properties; [CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration; public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null) { + NamingNormalizers = new TypeList(); _fallbackConfiguration = fallbackConfiguration; _properties = new Dictionary(); } @@ -68,4 +72,4 @@ namespace Volo.Abp.BlobStoring return this; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs index ab27aab01f..7a4da39ef9 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs @@ -1,4 +1,5 @@ -using Volo.Abp.DependencyInjection; +using System; +using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; @@ -7,36 +8,41 @@ namespace Volo.Abp.BlobStoring public class BlobContainerFactory : IBlobContainerFactory, ITransientDependency { protected IBlobProviderSelector ProviderSelector { get; } - + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } protected ICurrentTenant CurrentTenant { get; } - + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IServiceProvider ServiceProvider { get; } + public BlobContainerFactory( IBlobContainerConfigurationProvider configurationProvider, ICurrentTenant currentTenant, - ICancellationTokenProvider cancellationTokenProvider, - IBlobProviderSelector providerSelector) + ICancellationTokenProvider cancellationTokenProvider, + IBlobProviderSelector providerSelector, + IServiceProvider serviceProvider) { ConfigurationProvider = configurationProvider; CurrentTenant = currentTenant; CancellationTokenProvider = cancellationTokenProvider; ProviderSelector = providerSelector; + ServiceProvider = serviceProvider; } - + public virtual IBlobContainer Create(string name) { var configuration = ConfigurationProvider.Get(name); - + return new BlobContainer( name, configuration, ProviderSelector.Get(name), CurrentTenant, - CancellationTokenProvider + CancellationTokenProvider, + ServiceProvider ); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs new file mode 100644 index 0000000000..2bdc03507d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizer.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.BlobStoring +{ + public interface IBlobNamingNormalizer + { + string NormalizeContainerName(string containerName); + + string NormalizeBlobName(string blobName); + } +} diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj new file mode 100644 index 0000000000..c362c16bff --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo.Abp.Caching.StackExchangeRedis.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0 + Volo.Abp.Caching.StackExchangeRedis + Volo.Abp.Caching.StackExchangeRedis + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs new file mode 100644 index 0000000000..b0f7d68ee4 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DependsOn( + typeof(AbpCachingModule) + )] + public class AbpCachingStackExchangeRedisModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + context.Services.AddStackExchangeRedisCache(options => + { + var redisConfiguration = configuration["Redis:Configuration"]; + if (!redisConfiguration.IsNullOrEmpty()) + { + options.Configuration = configuration["Redis:Configuration"]; + } + }); + + context.Services.Replace(ServiceDescriptor.Singleton()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs new file mode 100644 index 0000000000..c3cb34f30c --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.StackExchangeRedis; +using Microsoft.Extensions.Options; +using StackExchange.Redis; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DisableConventionalRegistration] + public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems + { + protected static readonly string SetScript; + protected static readonly string AbsoluteExpirationKey; + protected static readonly string SlidingExpirationKey; + protected static readonly string DataKey; + protected static readonly long NotPresent; + + private static readonly FieldInfo RedisDatabaseField; + private static readonly MethodInfo ConnectMethod; + private static readonly MethodInfo ConnectAsyncMethod; + private static readonly MethodInfo MapMetadataMethod; + private static readonly MethodInfo GetAbsoluteExpirationMethod; + private static readonly MethodInfo GetExpirationInSecondsMethod; + + protected IDatabase RedisDatabase => GetRedisDatabase(); + private IDatabase _redisDatabase; + + protected string Instance { get; } + + static AbpRedisCache() + { + var type = typeof(RedisCache); + + RedisDatabaseField = type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic); + + ConnectMethod = type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic); + + ConnectAsyncMethod = type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic); + + MapMetadataMethod = type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic); + + GetAbsoluteExpirationMethod = type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic); + + GetExpirationInSecondsMethod = type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic); + + SetScript = type.GetField("SetScript", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString(); + + NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).To(); + } + + public AbpRedisCache(IOptions optionsAccessor) + : base(optionsAccessor) + { + Instance = optionsAccessor.Value.InstanceName ?? string.Empty; + } + + protected virtual void Connect() + { + if (GetRedisDatabase() != null) + { + return; + } + + ConnectMethod.Invoke(this, Array.Empty()); + } + + protected virtual Task ConnectAsync(CancellationToken token = default) + { + if (GetRedisDatabase() != null) + { + return Task.CompletedTask; + } + + return (Task) ConnectAsyncMethod.Invoke(this, new object[] {token}); + } + + public byte[][] GetMany( + IEnumerable keys) + { + keys = Check.NotNull(keys, nameof(keys)); + + return GetAndRefreshMany(keys, true); + } + + public async Task GetManyAsync( + IEnumerable keys, + CancellationToken token = default) + { + keys = Check.NotNull(keys, nameof(keys)); + + return await GetAndRefreshManyAsync(keys, true, token); + } + + public void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options) + { + Connect(); + + Task.WaitAll(PipelineSetMany(items, options)); + } + + public async Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options, + CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + await ConnectAsync(token); + + await Task.WhenAll(PipelineSetMany(items, options)); + } + + protected virtual byte[][] GetAndRefreshMany( + IEnumerable keys, + bool getData) + { + Connect(); + + var keyArray = keys.Select(key => Instance + key).ToArray(); + RedisValue[][] results; + + if (getData) + { + results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey, DataKey); + } + else + { + results = RedisDatabase.HashMemberGetMany(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey); + } + + Task.WaitAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); + + return bytes; + } + + protected virtual async Task GetAndRefreshManyAsync( + IEnumerable keys, + bool getData, + CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + await ConnectAsync(token); + + var keyArray = keys.Select(key => Instance + key).ToArray(); + RedisValue[][] results; + + if (getData) + { + results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey, DataKey); + } + else + { + results = await RedisDatabase.HashMemberGetManyAsync(keyArray, AbsoluteExpirationKey, + SlidingExpirationKey); + } + + await Task.WhenAll(PipelineRefreshManyAndOutData(keyArray, results, out var bytes)); + + return bytes; + } + + protected virtual Task[] PipelineRefreshManyAndOutData( + string[] keys, + RedisValue[][] results, + out byte[][] bytes) + { + bytes = new byte[keys.Length][]; + var tasks = new Task[keys.Length]; + + for (var i = 0; i < keys.Length; i++) + { + if (results[i].Length >= 2) + { + MapMetadata(results[i], out DateTimeOffset? absExpr, out TimeSpan? sldExpr); + + if (sldExpr.HasValue) + { + TimeSpan? expr; + + if (absExpr.HasValue) + { + var relExpr = absExpr.Value - DateTimeOffset.Now; + expr = relExpr <= sldExpr.Value ? relExpr : sldExpr; + } + else + { + expr = sldExpr; + } + + tasks[i] = RedisDatabase.KeyExpireAsync(keys[i], expr); + } + else + { + tasks[i] = Task.CompletedTask; + } + } + + if (results[i].Length >= 3 && results[i][2].HasValue) + { + bytes[i] = results[i][2]; + } + else + { + bytes[i] = null; + } + } + + return tasks; + } + + protected virtual Task[] PipelineSetMany( + IEnumerable> items, + DistributedCacheEntryOptions options) + { + items = Check.NotNull(items, nameof(items)); + options = Check.NotNull(options, nameof(options)); + + var itemArray = items.ToArray(); + var tasks = new Task[itemArray.Length]; + var creationTime = DateTimeOffset.UtcNow; + var absoluteExpiration = GetAbsoluteExpiration(creationTime, options); + + for (var i = 0; i < itemArray.Length; i++) + { + tasks[i] = RedisDatabase.ScriptEvaluateAsync(SetScript, new RedisKey[] {Instance + itemArray[i].Key}, + new RedisValue[] + { + absoluteExpiration?.Ticks ?? NotPresent, + options.SlidingExpiration?.Ticks ?? NotPresent, + GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, + itemArray[i].Value + }); + } + + return tasks; + } + + protected virtual void MapMetadata( + RedisValue[] results, + out DateTimeOffset? absoluteExpiration, + out TimeSpan? slidingExpiration) + { + var parameters = new object[] {results, null, null}; + MapMetadataMethod.Invoke(this, parameters); + + absoluteExpiration = (DateTimeOffset?) parameters[1]; + slidingExpiration = (TimeSpan?) parameters[2]; + } + + protected virtual long? GetExpirationInSeconds( + DateTimeOffset creationTime, + DateTimeOffset? absoluteExpiration, + DistributedCacheEntryOptions options) + { + return (long?) GetExpirationInSecondsMethod.Invoke(null, + new object[] {creationTime, absoluteExpiration, options}); + } + + protected virtual DateTimeOffset? GetAbsoluteExpiration( + DateTimeOffset creationTime, + DistributedCacheEntryOptions options) + { + return (DateTimeOffset?) GetAbsoluteExpirationMethod.Invoke(null, new object[] {creationTime, options}); + } + + private IDatabase GetRedisDatabase() + { + if (_redisDatabase == null) + { + _redisDatabase = RedisDatabaseField.GetValue(this) as IDatabase; + } + + return _redisDatabase; + } + } +} diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs new file mode 100644 index 0000000000..ad077a7440 --- /dev/null +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisExtensions.cs @@ -0,0 +1,47 @@ +using System.Linq; +using System.Threading.Tasks; +using StackExchange.Redis; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public static class AbpRedisExtensions + { + public static RedisValue[][] HashMemberGetMany( + this IDatabase cache, + string[] keys, + params string[] members) + { + var tasks = new Task[keys.Length]; + var fields = members.Select(member => (RedisValue) member).ToArray(); + var results = new RedisValue[keys.Length][]; + + for (var i = 0; i < keys.Length; i++) + { + tasks[i] = cache.HashGetAsync((RedisKey) keys[i], fields); + } + + for (var i = 0; i < tasks.Length; i++) + { + results[i] = cache.Wait(tasks[i]); + } + + return results; + } + + public static async Task HashMemberGetManyAsync( + this IDatabase cache, + string[] keys, + params string[] members) + { + var tasks = new Task[keys.Length]; + var fields = members.Select(member => (RedisValue) member).ToArray(); + + for (var i = 0; i < keys.Length; i++) + { + tasks[i] = cache.HashGetAsync((RedisKey) keys[i], fields); + } + + return await Task.WhenAll(tasks); + } + } +} diff --git a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj index db744f410e..5ee096a493 100644 --- a/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj +++ b/framework/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 267ab259f5..5bb1dd2481 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -28,16 +31,16 @@ namespace Volo.Abp.Caching IDistributedCacheSerializer serializer, IDistributedCacheKeyNormalizer keyNormalizer, IHybridServiceScopeFactory serviceScopeFactory) : base( - distributedCacheOption: distributedCacheOption, - cache: cache, - cancellationTokenProvider: cancellationTokenProvider, - serializer: serializer, - keyNormalizer: keyNormalizer, - serviceScopeFactory: serviceScopeFactory) + distributedCacheOption: distributedCacheOption, + cache: cache, + cancellationTokenProvider: cancellationTokenProvider, + serializer: serializer, + keyNormalizer: keyNormalizer, + serviceScopeFactory: serviceScopeFactory) { } - } + /// /// Represents a distributed cache of type. /// Uses a generic cache key type of type. @@ -148,19 +151,151 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return null; } throw; } - if (cachedBytes == null) + return ToCacheItem(cachedBytes); + } + + public virtual KeyValuePair[] GetMany( + IEnumerable keys, + bool? hideErrors = null) + { + var keyArray = keys.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) { - return null; + return GetManyFallback( + keyArray, + hideErrors + ); } - return Serializer.Deserialize(cachedBytes); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + byte[][] cachedBytes; + + try + { + cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey)); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return ToCacheItemsWithDefaultValues(keyArray); + } + + throw; + } + + return ToCacheItems(cachedBytes, keyArray); + } + + protected virtual KeyValuePair[] GetManyFallback( + TCacheKey[] keys, + bool? hideErrors = null) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + return keys + .Select(key => new KeyValuePair( + key, + Get(key, hideErrors: false) + ) + ).ToArray(); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return ToCacheItemsWithDefaultValues(keys); + } + + throw; + } + } + + public virtual async Task[]> GetManyAsync( + IEnumerable keys, + bool? hideErrors = null, + CancellationToken token = default) + { + var keyArray = keys.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + return await GetManyFallbackAsync( + keyArray, + hideErrors, + token + ); + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + byte[][] cachedBytes; + + try + { + cachedBytes = await cacheSupportsMultipleItems.GetManyAsync( + keyArray.Select(NormalizeKey), + CancellationTokenProvider.FallbackToProvider(token) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return ToCacheItemsWithDefaultValues(keyArray); + } + + throw; + } + + return ToCacheItems(cachedBytes, keyArray); + } + + protected virtual async Task[]> GetManyFallbackAsync( + TCacheKey[] keys, + bool? hideErrors = null, + CancellationToken token = default) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + var result = new List>(); + + foreach (var key in keys) + { + result.Add(new KeyValuePair( + key, + await GetAsync(key, false, token)) + ); + } + + return result.ToArray(); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return ToCacheItemsWithDefaultValues(keys); + } + + throw; + } } /// @@ -307,7 +442,7 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } @@ -354,14 +489,156 @@ namespace Volo.Abp.Caching } } - /// - /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. + public void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null) + { + var itemsArray = items.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + SetManyFallback( + itemsArray, + options, + hideErrors + ); + + return; + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + cacheSupportsMultipleItems.SetMany( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } + } + + protected virtual void SetManyFallback( + KeyValuePair[] items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + foreach (var item in items) + { + Set( + item.Key, + item.Value, + options: options, + hideErrors: false + ); + } + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } + } + + public virtual async Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default) + { + var itemsArray = items.ToArray(); + + var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems; + if (cacheSupportsMultipleItems == null) + { + await SetManyFallbackAsync( + itemsArray, + options, + hideErrors, + token + ); + + return; + } + + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + await cacheSupportsMultipleItems.SetManyAsync( + ToRawCacheItems(itemsArray), + options ?? DefaultCacheOptions, + CancellationTokenProvider.FallbackToProvider(token) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } + } + + protected virtual async Task SetManyFallbackAsync( + KeyValuePair[] items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default) + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + foreach (var item in items) + { + await SetAsync( + item.Key, + item.Value, + options: options, + hideErrors: false, + token: token + ); + } + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } + } + public virtual void Refresh( TCacheKey key, bool? - hideErrors = null) + hideErrors = null) { hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; @@ -373,20 +650,14 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } throw; } } - /// - /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. - /// The for the task. - /// The indicating that the operation is asynchronous. + public virtual async Task RefreshAsync( TCacheKey key, bool? hideErrors = null, @@ -410,11 +681,6 @@ namespace Volo.Abp.Caching } } - /// - /// Removes the cache item for given key from cache. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Remove( TCacheKey key, bool? hideErrors = null) @@ -429,7 +695,7 @@ namespace Volo.Abp.Caching { if (hideErrors == true) { - AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + HandleException(ex); return; } @@ -437,13 +703,6 @@ namespace Volo.Abp.Caching } } - /// - /// Removes the cache item for given key from cache. - /// - /// The key of cached item to be retrieved from the cache. - /// Indicates to throw or hide the exceptions for the distributed cache. - /// The for the task. - /// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync( TCacheKey key, bool? hideErrors = null, @@ -467,6 +726,11 @@ namespace Volo.Abp.Caching } } + protected virtual void HandleException(Exception ex) + { + AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); + } + protected virtual async Task HandleExceptionAsync(Exception ex) { Logger.LogException(ex, LogLevel.Warning); @@ -478,5 +742,56 @@ namespace Volo.Abp.Caching .NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning)); } } + + protected virtual KeyValuePair[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys) + { + if (itemBytes.Length != itemKeys.Length) + { + throw new AbpException("count of the item bytes should be same with the count of the given keys"); + } + + var result = new List>(); + + for (int i = 0; i < itemKeys.Length; i++) + { + result.Add( + new KeyValuePair( + itemKeys[i], + ToCacheItem(itemBytes[i]) + ) + ); + } + + return result.ToArray(); + } + + [CanBeNull] + protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes) + { + if (bytes == null) + { + return null; + } + + return Serializer.Deserialize(bytes); + } + + + protected virtual KeyValuePair[] ToRawCacheItems(KeyValuePair[] items) + { + return items + .Select(i => new KeyValuePair( + NormalizeKey(i.Key), + Serializer.Serialize(i.Value) + ) + ).ToArray(); + } + + private static KeyValuePair[] ToCacheItemsWithDefaultValues(TCacheKey[] keys) + { + return keys + .Select(key => new KeyValuePair(key, default)) + .ToArray(); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs new file mode 100644 index 0000000000..af6f87021b --- /dev/null +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; + +namespace Volo.Abp.Caching +{ + public interface ICacheSupportsMultipleItems + { + byte[][] GetMany( + IEnumerable keys + ); + + Task GetManyAsync( + IEnumerable keys, + CancellationToken token = default + ); + + void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options + ); + + Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options, + CancellationToken token = default + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 163da05632..80fe124785 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -34,6 +35,39 @@ namespace Volo.Abp.Caching TCacheKey key, bool? hideErrors = null ); + + /// + /// Gets multiple cache items with the given keys. + /// + /// The returned list contains exactly the same count of items specified in the given keys. + /// An item in the return list can not be null, but an item in the list has null value + /// if the related key not found in the cache. + /// + /// The keys of cached items to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// List of cache items. + KeyValuePair[] GetMany( + IEnumerable keys, + bool? hideErrors = null + ); + + /// + /// Gets multiple cache items with the given keys. + /// + /// The returned list contains exactly the same count of items specified in the given keys. + /// An item in the return list can not be null, but an item in the list has null value + /// if the related key not found in the cache. + /// + /// + /// The keys of cached items to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// /// The for the task. + /// List of cache items. + Task[]> GetManyAsync( + IEnumerable keys, + bool? hideErrors = null, + CancellationToken token = default + ); /// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. @@ -113,6 +147,35 @@ namespace Volo.Abp.Caching CancellationToken token = default ); + /// + /// Sets multiple cache items. + /// Based on the implementation, this can be more efficient than setting multiple items individually. + /// + /// Items to set on the cache + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + void SetMany( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null + ); + + /// + /// Sets multiple cache items. + /// Based on the implementation, this can be more efficient than setting multiple items individually. + /// + /// Items to set on the cache + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. + Task SetManyAsync( + IEnumerable> items, + DistributedCacheEntryOptions options = null, + bool? hideErrors = null, + CancellationToken token = default + ); + /// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. /// diff --git a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj index 24f7fbe682..7e742c0467 100644 --- a/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj +++ b/framework/src/Volo.Abp.Castle.Core/Volo.Abp.Castle.Core.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj index 0b9306be91..40eecfd06a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj +++ b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj @@ -16,11 +16,11 @@ - - + + - - + + diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs index 2b524d9272..b11e3a54f4 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs @@ -51,17 +51,17 @@ namespace Volo.Abp.Cli.Commands var apiUrl = commandLineArgs.Options.GetOrNull(Options.ApiUrl.Short, Options.ApiUrl.Long); if (string.IsNullOrWhiteSpace(apiUrl)) { - var environmentJson = File.ReadAllText("src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " "); + var environmentJson = File.ReadAllText("projects/dev-app/src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " "); var environment = JObject.Parse(environmentJson); apiUrl = environment["apis"]["default"]["url"].ToString(); } - apiUrl += "/api/abp/api-definition?IncludeTypes=true"; + apiUrl = apiUrl.EnsureEndsWith('/') + "api/abp/api-definition?IncludeTypes=true"; var uiFramework = GetUiFramework(commandLineArgs); output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long); if (!string.IsNullOrWhiteSpace(output) && !(output.EndsWith("/") || output.EndsWith("\\"))) - { + { output += "/"; } @@ -110,7 +110,7 @@ namespace Volo.Abp.Cli.Commands if (rootPath != "app") { Logger.LogInformation($"{rootPath} directory is creating"); - } + } if (rootPath == "app") { @@ -469,17 +469,21 @@ namespace Volo.Abp.Cli.Commands if (returnValueType.Contains("<")) { returnValueType = returnValueType.Split('<')[1].Split('>')[0]; + var clrType = Type.GetType(returnValueType, throwOnError: false); + if (clrType != null && TypeHelper.IsPrimitiveExtended(clrType, includeEnums: true)) + { + return null; + } + if (returnValueType.StartsWith("Volo.Abp.Application.Dtos") - || returnValueType.StartsWith("System.Collections") - || returnValueType == "System.String" - || returnValueType == "System.Void" - || returnValueType.Contains("System.Net.HttpStatusCode?") - || returnValueType.Contains("IActionResult") - || returnValueType.Contains("ActionResult") - || returnValueType.Contains("IStringValueType") - || returnValueType.Contains("IValueValidator") - || returnValueType.Contains("Guid") - ) + || returnValueType.StartsWith("System.Collections") + || returnValueType == "System.Void" + || returnValueType.Contains("System.Net.HttpStatusCode?") + || returnValueType.Contains("IActionResult") + || returnValueType.Contains("ActionResult") + || returnValueType.Contains("IStringValueType") + || returnValueType.Contains("IValueValidator") + ) { return null; } @@ -536,7 +540,7 @@ namespace Volo.Abp.Cli.Commands modelFileText.AppendLine(Environment.NewLine); modelFileText.AppendLine($"import {{ {baseTypeName} }} from '{baseTypeKebabCase}';"); - + extends = "extends " + (!string.IsNullOrWhiteSpace(customBaseTypeName) ? customBaseTypeName : baseTypeName); var modelIndex = CreateType(data, baseType, rootPath, modelIndexList, controllerPathName); @@ -628,7 +632,7 @@ namespace Volo.Abp.Cli.Commands { from = "./" + propertyTypeKebabCase; } - + modelFileText.Insert(0, $"import {{ {propertyType} }} from '{from}';"); modelFileText.Insert(0, Environment.NewLine); modelIndexList.Add(modelIndex); @@ -816,4 +820,4 @@ namespace Volo.Abp.Cli.Commands public bool IsOptional { get; set; } public string BindingSourceId { get; set; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index b4a4ff8bdd..fbe7fa675f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -59,15 +59,33 @@ namespace Volo.Abp.Cli.Commands return; } - var result = CmdHelper.RunCmd("dotnet tool install " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + try + { + var result = CmdHelper.RunCmd("dotnet tool install " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); - if (result == 0) + if (result == 0) + { + Logger.LogInformation("ABP Suite has been successfully installed."); + Logger.LogInformation("You can run it with the CLI command \"abp suite\""); + } + else + { + ShowSuiteManualInstallCommand(); + } + } + catch (Exception e) { - Logger.LogInformation("ABP Suite has been successfully installed."); - Logger.LogInformation("You can run it with the CLI command \"abp suite\""); + Logger.LogError("Couldn't install ABP Suite." + e.Message); + ShowSuiteManualInstallCommand(); } } + private void ShowSuiteManualInstallCommand() + { + Logger.LogInformation("You can also run the following command to install ABP Suite."); + Logger.LogInformation("dotnet tool install -g Volo.Abp.Suite"); + } + private async Task UpdateSuiteAsync() { var nugetIndexUrl = await _nuGetIndexUrlService.GetAsync(); @@ -77,7 +95,26 @@ namespace Volo.Abp.Cli.Commands return; } - CmdHelper.RunCmd("dotnet tool update " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + try + { + var result = CmdHelper.RunCmd("dotnet tool update " + SuitePackageName + " --add-source " + nugetIndexUrl + " -g"); + + if (result != 0) + { + ShowSuiteManualUpdateCommand(); + } + } + catch (Exception ex) + { + Logger.LogError("Couldn't update ABP Suite." + ex.Message); + ShowSuiteManualUpdateCommand(); + } + } + + private void ShowSuiteManualUpdateCommand() + { + Logger.LogError("You can also run the following command to update ABP Suite."); + Logger.LogError("dotnet tool update -g Volo.Abp.Suite"); } private static void RemoveSuite() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index ca8bfa9bf8..5fe255828e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -63,11 +63,13 @@ namespace Volo.Abp.Cli.Commands solution = Directory.GetFiles(directory, "*.sln", SearchOption.AllDirectories).FirstOrDefault(); } + var checkAll = commandLineArgs.Options.ContainsKey(Options.CheckAll.Long); + if (solution != null) { var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); - await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, includePreviews); + await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, includePreviews, checkAll: checkAll); Logger.LogInformation($"Volo packages are updated in {solutionName} solution."); return; @@ -79,7 +81,7 @@ namespace Volo.Abp.Cli.Commands { var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); - await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, includePreviews); + await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, includePreviews, checkAll: checkAll); Logger.LogInformation($"Volo packages are updated in {projectName} project."); return; @@ -107,6 +109,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine("--nuget (Only updates Nuget packages)"); sb.AppendLine("-sp|--solution-path (Specify the solution path)"); sb.AppendLine("-sn|--solution-name (Specify the solution name)"); + sb.AppendLine("--check-all (Check the new version of each package separately)"); sb.AppendLine(""); sb.AppendLine("Some examples:"); sb.AppendLine(""); @@ -149,6 +152,11 @@ namespace Volo.Abp.Cli.Commands public const string Npm = "npm"; public const string NuGet = "nuget"; } + + public static class CheckAll + { + public const string Long = "check-all"; + } } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs index 9a76f9ee21..b69ce479ac 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AngularEnvironmentFilePortChangeForSeparatedIdentityServersStep.cs +++ b/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/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)) + (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)) ) .ToList(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index 1a279fafc7..be5b553744 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -52,33 +53,35 @@ namespace Volo.Abp.Cli.ProjectModification _npmGlobalPackagesChecker.Check(); - foreach (var file in fileList) + var packagesUpdated = new ConcurrentDictionary(); + + async Task UpdateAsync(string file) { - var packagesUpdated = await UpdatePackagesInFile(file, includePreviews, switchToStable); + var updated = await UpdatePackagesInFile(file, includePreviews, switchToStable); + packagesUpdated.TryAdd(file, updated); + } - if (packagesUpdated) - { - var fileDirectory = Path.GetDirectoryName(file).EnsureEndsWith(Path.DirectorySeparatorChar); - - if (IsAngularProject(fileDirectory)) - { - if (includePreviews) - { - await CreateNpmrcFileAsync(Path.GetDirectoryName(file)); - } - else if (switchToStable) - { - await DeleteNpmrcFileAsync(Path.GetDirectoryName(file)); - } - } + Task.WaitAll(fileList.Select(UpdateAsync).ToArray()); + + foreach (var file in packagesUpdated.Where(x => x.Value)) + { + var fileDirectory = Path.GetDirectoryName(file.Key).EnsureEndsWith(Path.DirectorySeparatorChar); + if (includePreviews) + { + await CreateNpmrcFileAsync(Path.GetDirectoryName(file.Key)); + RunNpmInstall(fileDirectory); + } + else if (switchToStable) + { + await DeleteNpmrcFileAsync(Path.GetDirectoryName(file.Key)); RunYarn(fileDirectory); + } - if (!IsAngularProject(fileDirectory)) - { - Thread.Sleep(500); - RunGulp(fileDirectory); - } + if (!IsAngularProject(fileDirectory)) + { + Thread.Sleep(1000); + RunGulp(fileDirectory); } } } @@ -161,7 +164,8 @@ namespace Volo.Abp.Cli.ProjectModification return File.Exists(Path.Combine(fileDirectory, "angular.json")); } - protected virtual async Task UpdatePackagesInFile(string filePath, bool includePreviews = false, bool switchToStable = false) + protected virtual async Task UpdatePackagesInFile(string filePath, bool includePreviews = false, + bool switchToStable = false) { var packagesUpdated = false; var fileContent = File.ReadAllText(filePath); @@ -196,67 +200,54 @@ namespace Volo.Abp.Cli.ProjectModification bool includePreviews = false, bool switchToStable = false) { - var currentVersion = (string)package.Value; - - var version = await GetLatestVersion(package, currentVersion, includePreviews, switchToStable); + var currentVersion = (string) package.Value; - var versionWithPrefix = $"~{version}"; + var version = ""; + if (includePreviews || (!switchToStable && currentVersion.Contains("-preview"))) + { + version = "preview"; + } + else + { + version = await GetLatestVersion(package); + } - if (versionWithPrefix == currentVersion) + if (version == currentVersion) { return false; } - package.Value.Replace(versionWithPrefix); + package.Value.Replace(version); - Logger.LogInformation($"Updated {package.Name} to {version} in {filePath.Replace(Directory.GetCurrentDirectory(), "")}."); + Logger.LogInformation( + $"Updated {package.Name} to {version} in {filePath.Replace(Directory.GetCurrentDirectory(), "")}."); return true; } protected virtual async Task GetLatestVersion( - JProperty package, - string currentVersion, - bool includePreviews = false, - bool switchToStable = false) + JProperty package) { if (_fileVersionStorage.ContainsKey(package.Name)) { return _fileVersionStorage[package.Name]; } - var newVersion = currentVersion; - - if (includePreviews || (!switchToStable && currentVersion.Contains("-preview"))) - { - if (_myGetApiResponse == null) - { - _myGetApiResponse = await _myGetPackageListFinder.GetPackagesAsync(); - } - - var mygetPackage = _myGetApiResponse.Packages.FirstOrDefault(p => p.Id == package.Name); - if (mygetPackage != null) - { - newVersion = mygetPackage.Versions.Last(); - } - } - else - { - newVersion = CmdHelper.RunCmdAndGetOutput($"npm show {package.Name} version"); - } + var newVersion = CmdHelper.RunCmdAndGetOutput($"npm show {package.Name} version"); + var newVersionWithPrefix = $"~{newVersion}"; - _fileVersionStorage[package.Name] = newVersion; + _fileVersionStorage[package.Name] = newVersionWithPrefix; - return newVersion; + return $"~{newVersionWithPrefix}"; } protected virtual List GetAbpPackagesFromPackageJson(JObject fileObject) { - var dependencyList = new[] { "dependencies", "devDependencies", "peerDependencies" }; + var dependencyList = new[] {"dependencies", "devDependencies", "peerDependencies"}; var abpPackages = new List(); foreach (var dependencyListName in dependencyList) { - var dependencies = (JObject)fileObject[dependencyListName]; + var dependencies = (JObject) fileObject[dependencyListName]; if (dependencies == null) { @@ -264,7 +255,8 @@ namespace Volo.Abp.Cli.ProjectModification } var properties = dependencies.Properties().ToList(); - abpPackages.AddRange(properties.Where(p => p.Name.StartsWith("@abp/") || p.Name.StartsWith("@volo/")).ToList()); + abpPackages.AddRange(properties.Where(p => p.Name.StartsWith("@abp/") || p.Name.StartsWith("@volo/")) + .ToList()); } return abpPackages; @@ -281,5 +273,11 @@ namespace Volo.Abp.Cli.ProjectModification Logger.LogInformation($"Running Yarn on {fileDirectory}"); CmdHelper.RunCmd($"cd {fileDirectory} && yarn"); } + + protected virtual void RunNpmInstall(string fileDirectory) + { + Logger.LogInformation($"Running npm install on {fileDirectory}"); + CmdHelper.RunCmd($"cd {fileDirectory} && npm install"); + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index fb2a386ef9..9661dceddd 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using NuGet.Versioning; using System.IO; using System.Linq; @@ -24,19 +25,47 @@ namespace Volo.Abp.Cli.ProjectModification Logger = NullLogger.Instance; } - public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool switchToStable = false) + public async Task UpdateSolutionAsync(string solutionPath, bool includePreviews = false, bool switchToStable = false, bool checkAll = false) { var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); - foreach (var filePath in projectPaths) + if (checkAll) { - await UpdateInternalAsync(filePath, includePreviews, switchToStable); + Task.WaitAll(projectPaths.Select(projectPath => UpdateInternalAsync(projectPath, includePreviews, switchToStable)).ToArray()); + } + else + { + var latestVersionFromNuget = await _nuGetService.GetLatestVersionOrNullAsync("Volo.Abp.Core"); + var latestVersionFromMyGet = await GetLatestVersionFromMyGet("Volo.Abp.Core"); + + async Task UpdateAsync(string filePath) + { + var fileContent = File.ReadAllText(filePath); + var updatedContent = await UpdateVoloPackagesAsync(fileContent, includePreviews, switchToStable, latestVersionFromNuget, latestVersionFromMyGet); + + File.WriteAllText(filePath, updatedContent); + } + + Task.WaitAll(projectPaths.Select(UpdateAsync).ToArray()); } } - public async Task UpdateProjectAsync(string projectPath, bool includePreviews = false, bool switchToStable = false) + public async Task UpdateProjectAsync(string projectPath, bool includePreviews = false, bool switchToStable = false, bool checkAll = false) { - await UpdateInternalAsync(projectPath, includePreviews, switchToStable); + if (checkAll) + { + await UpdateInternalAsync(projectPath, includePreviews, switchToStable); + } + else + { + var latestVersionFromNuget = await _nuGetService.GetLatestVersionOrNullAsync("Volo.Abp.Core"); + var latestVersionFromMyGet = await GetLatestVersionFromMyGet("Volo.Abp.Core"); + + var fileContent = File.ReadAllText(projectPath); + var updatedContent = await UpdateVoloPackagesAsync(fileContent, includePreviews, switchToStable, latestVersionFromNuget, latestVersionFromMyGet); + + File.WriteAllText(projectPath, updatedContent); + } } protected virtual async Task UpdateInternalAsync(string projectPath, bool includePreviews = false, bool switchToStable = false) @@ -47,7 +76,7 @@ namespace Volo.Abp.Cli.ProjectModification File.WriteAllText(projectPath, updatedContent); } - private async Task UpdateVoloPackagesAsync(string content, bool includePreviews = false, bool switchToStable = false) + private async Task UpdateVoloPackagesAsync(string content, bool includePreviews = false, bool switchToStable = false, SemanticVersion latestNugetVersion = null, string latestMyGetVersion = null) { string packageId = null; @@ -81,7 +110,7 @@ namespace Volo.Abp.Cli.ProjectModification if (includePreviews || (currentVersion.Contains("-preview") && !switchToStable)) { - var latestVersion = await GetLatestVersionFromMyGet(packageId); + var latestVersion = latestMyGetVersion ?? await GetLatestVersionFromMyGet(packageId); if (currentVersion != latestVersion) { @@ -95,7 +124,7 @@ namespace Volo.Abp.Cli.ProjectModification } else { - var latestVersion = await _nuGetService.GetLatestVersionOrNullAsync(packageId); + var latestVersion = latestNugetVersion ?? await _nuGetService.GetLatestVersionOrNullAsync(packageId); if (latestVersion != null && (currentVersion.Contains("-preview") || currentSemanticVersion < latestVersion)) { @@ -109,7 +138,7 @@ namespace Volo.Abp.Cli.ProjectModification } } - return await Task.FromResult(doc.OuterXml); + return doc.OuterXml; } } catch (Exception ex) diff --git a/framework/src/Volo.Abp.Cli/Properties/launchSettings.json b/framework/src/Volo.Abp.Cli/Properties/launchSettings.json deleted file mode 100644 index e7f99eea04..0000000000 --- a/framework/src/Volo.Abp.Cli/Properties/launchSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "profiles": { - "Volo.Abp.Cli": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj b/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj index aec34db41a..424e09b115 100644 --- a/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj +++ b/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs b/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs new file mode 100644 index 0000000000..60240159f3 --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + public interface IOSPlatformProvider + { + OSPlatform GetCurrentOSPlatform(); + } +} diff --git a/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs b/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs new file mode 100644 index 0000000000..89b57acaab --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using Volo.Abp.DependencyInjection; + +namespace System.Runtime +{ + public class OSPlatformProvider : IOSPlatformProvider, ITransientDependency + { + public virtual OSPlatform GetCurrentOSPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OSPlatform.OSX; //MAC + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OSPlatform.Windows; + } + + return OSPlatform.Linux; + } + } +} diff --git a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj index 50fb328387..000433c83d 100644 --- a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj +++ b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj @@ -15,20 +15,20 @@ - - - - - - - - - - + + + + + + + + + + - + - + diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs index 6d53ddc954..8906859edf 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/ITypeList.cs @@ -27,7 +27,7 @@ namespace Volo.Abp.Collections /// Adds a type to list if it's not already in the list. /// /// Type - void TryAdd() where T : TBaseType; + bool TryAdd() where T : TBaseType; /// /// Checks if a type exists in the list. diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs index 24e66085f7..1f4b7ddc40 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Collections/TypeList.cs @@ -60,14 +60,15 @@ namespace Volo.Abp.Collections _typeList.Add(typeof(T)); } - public void TryAdd() where T : TBaseType + public bool TryAdd() where T : TBaseType { if (Contains()) { - return; + return false; } Add(); + return true; } /// diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs index 83182fc965..83c2c4c38e 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs @@ -359,5 +359,15 @@ namespace Volo.Abp.Reflection ? type.GenericTypeArguments[0] : type; } + + public static bool IsDefaultValue([CanBeNull] object obj) + { + if (obj == null) + { + return true; + } + + return obj.Equals(GetDefaultValue(obj.GetType())); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs new file mode 100644 index 0000000000..59d5bcfa47 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation +{ + public class DynamicMaxLengthAttribute : MaxLengthAttribute + { + private static readonly FieldInfo MaximumLengthField; + + static DynamicMaxLengthAttribute() + { + MaximumLengthField = typeof(MaxLengthAttribute).GetField( + "k__BackingField", + BindingFlags.Instance | BindingFlags.NonPublic + ); + Debug.Assert(MaximumLengthField != null, nameof(MaximumLengthField) + " != null"); + } + + public DynamicMaxLengthAttribute( + [NotNull] Type sourceType, + [CanBeNull] string maximumLengthPropertyName) + { + Check.NotNull(sourceType, nameof(sourceType)); + + if (maximumLengthPropertyName != null) + { + var maximumLengthProperty = sourceType.GetProperty( + maximumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(maximumLengthProperty != null, nameof(maximumLengthProperty) + " != null"); + MaximumLengthField.SetValue(this, (int) maximumLengthProperty.GetValue(null)); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs new file mode 100644 index 0000000000..d27a04b1ba --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicStringLengthAttribute.cs @@ -0,0 +1,58 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation +{ + /// + /// Used to determine and + /// properties on the runtime. + /// + public class DynamicStringLengthAttribute : StringLengthAttribute + { + private static readonly FieldInfo MaximumLengthField; + + static DynamicStringLengthAttribute() + { + MaximumLengthField = typeof(StringLengthAttribute).GetField( + "k__BackingField", + BindingFlags.Instance | BindingFlags.NonPublic + ); + Debug.Assert(MaximumLengthField != null, nameof(MaximumLengthField) + " != null"); + } + + /// A type to get the values of the properties + /// The name of the public static property for the + /// The name of the public static property for the + public DynamicStringLengthAttribute( + [NotNull] Type sourceType, + [CanBeNull] string maximumLengthPropertyName, + [CanBeNull] string minimumLengthPropertyName = null) + : base(0) + { + Check.NotNull(sourceType, nameof(sourceType)); + + if (maximumLengthPropertyName != null) + { + var maximumLengthProperty = sourceType.GetProperty( + maximumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(maximumLengthProperty != null, nameof(maximumLengthProperty) + " != null"); + MaximumLengthField.SetValue(this, (int) maximumLengthProperty.GetValue(null)); + } + + if (minimumLengthPropertyName != null) + { + var minimumLengthProperty = sourceType.GetProperty( + minimumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(minimumLengthProperty != null, nameof(minimumLengthProperty) + " != null"); + MinimumLength = (int) minimumLengthProperty.GetValue(null); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj b/framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj index 27a92976e5..4feb946600 100644 --- a/framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj +++ b/framework/src/Volo.Abp.Dapper/Volo.Abp.Dapper.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateAppService.cs new file mode 100644 index 0000000000..a5d75b0b4b --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateAppService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Application.Services +{ + public interface ICreateAppService + : ICreateAppService + { + + } + + public interface ICreateAppService + : IApplicationService + { + Task CreateAsync(TCreateInput input); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateUpdateAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateUpdateAppService.cs new file mode 100644 index 0000000000..ce3698405c --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateUpdateAppService.cs @@ -0,0 +1,21 @@ +namespace Volo.Abp.Application.Services +{ + public interface ICreateUpdateAppService + : ICreateUpdateAppService + { + + } + + public interface ICreateUpdateAppService + : ICreateUpdateAppService + { + + } + + public interface ICreateUpdateAppService + : ICreateAppService, + IUpdateAppService + { + + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs index 12f3b51d11..10f88db22a 100644 --- a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Volo.Abp.Application.Dtos; namespace Volo.Abp.Application.Services @@ -28,16 +27,10 @@ namespace Volo.Abp.Application.Services } public interface ICrudAppService - : IApplicationService + : IReadOnlyAppService, + ICreateUpdateAppService, + IDeleteAppService { - Task GetAsync(TKey id); - Task> GetListAsync(TGetListInput input); - - Task CreateAsync(TCreateInput input); - - Task UpdateAsync(TKey id, TUpdateInput input); - - Task DeleteAsync(TKey id); } } diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IDeleteAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IDeleteAppService.cs new file mode 100644 index 0000000000..50badcead9 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IDeleteAppService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Application.Services +{ + public interface IDeleteAppService : IApplicationService + { + Task DeleteAsync(TKey id); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IReadOnlyAppService.cs new file mode 100644 index 0000000000..09df0dc9cd --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IReadOnlyAppService.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.Application.Services +{ + public interface IReadOnlyAppService + : IReadOnlyAppService + { + + } + + public interface IReadOnlyAppService + : IReadOnlyAppService + { + + } + + public interface IReadOnlyAppService + : IApplicationService + { + Task GetAsync(TKey id); + + Task> GetListAsync(TGetListInput input); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IUpdateAppService.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IUpdateAppService.cs new file mode 100644 index 0000000000..c0904709bf --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IUpdateAppService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Application.Services +{ + public interface IUpdateAppService + : IUpdateAppService + { + + } + + public interface IUpdateAppService + : IApplicationService + { + Task UpdateAsync(TKey id, TUpdateInput input); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs index e6f433cad6..11550c456f 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs @@ -1,9 +1,6 @@ using System; -using System.Linq; -using System.Linq.Dynamic.Core; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; -using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; @@ -61,17 +58,12 @@ namespace Volo.Abp.Application.Services } public abstract class AbstractKeyCrudAppService - : ApplicationService, + : AbstractKeyReadOnlyAppService, ICrudAppService where TEntity : class, IEntity { - protected IRepository Repository { get; } - protected virtual string GetPolicyName { get; set; } - - protected virtual string GetListPolicyName { get; set; } - protected virtual string CreatePolicyName { get; set; } protected virtual string UpdatePolicyName { get; set; } @@ -79,37 +71,11 @@ namespace Volo.Abp.Application.Services protected virtual string DeletePolicyName { get; set; } protected AbstractKeyCrudAppService(IRepository repository) + : base(repository) { Repository = repository; } - public virtual async Task GetAsync(TKey id) - { - await CheckGetPolicyAsync(); - - var entity = await GetEntityByIdAsync(id); - return MapToGetOutputDto(entity); - } - - public virtual async Task> GetListAsync(TGetListInput input) - { - await CheckGetListPolicyAsync(); - - var query = CreateFilteredQuery(input); - - var totalCount = await AsyncExecuter.CountAsync(query); - - query = ApplySorting(query, input); - query = ApplyPaging(query, input); - - var entities = await AsyncExecuter.ToListAsync(query); - - return new PagedResultDto( - totalCount, - entities.Select(MapToGetListOutputDto).ToList() - ); - } - public virtual async Task CreateAsync(TCreateInput input) { await CheckCreatePolicyAsync(); @@ -144,18 +110,6 @@ namespace Volo.Abp.Application.Services protected abstract Task DeleteByIdAsync(TKey id); - protected abstract Task GetEntityByIdAsync(TKey id); - - protected virtual async Task CheckGetPolicyAsync() - { - await CheckPolicyAsync(GetPolicyName); - } - - protected virtual async Task CheckGetListPolicyAsync() - { - await CheckPolicyAsync(GetListPolicyName); - } - protected virtual async Task CheckCreatePolicyAsync() { await CheckPolicyAsync(CreatePolicyName); @@ -171,101 +125,6 @@ namespace Volo.Abp.Application.Services await CheckPolicyAsync(DeletePolicyName); } - /// - /// Should apply sorting if needed. - /// - /// The query. - /// The input. - protected virtual IQueryable ApplySorting(IQueryable query, TGetListInput input) - { - //Try to sort query if available - if (input is ISortedResultRequest sortInput) - { - if (!sortInput.Sorting.IsNullOrWhiteSpace()) - { - return query.OrderBy(sortInput.Sorting); - } - } - - //IQueryable.Task requires sorting, so we should sort if Take will be used. - if (input is ILimitedResultRequest) - { - return ApplyDefaultSorting(query); - } - - //No sorting - return query; - } - - /// - /// Applies sorting if no sorting specified but a limited result requested. - /// - /// The query. - protected virtual IQueryable ApplyDefaultSorting(IQueryable query) - { - if (typeof(TEntity).IsAssignableTo()) - { - return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); - } - - throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyCrudAppService!"); - } - - /// - /// Should apply paging if needed. - /// - /// The query. - /// The input. - protected virtual IQueryable ApplyPaging(IQueryable query, TGetListInput input) - { - //Try to use paging if available - if (input is IPagedResultRequest pagedInput) - { - return query.PageBy(pagedInput); - } - - //Try to limit query result if available - if (input is ILimitedResultRequest limitedInput) - { - return query.Take(limitedInput.MaxResultCount); - } - - //No paging - return query; - } - - /// - /// This method should create based on given input. - /// It should filter query if needed, but should not do sorting or paging. - /// Sorting should be done in and paging should be done in - /// methods. - /// - /// The input. - protected virtual IQueryable CreateFilteredQuery(TGetListInput input) - { - return Repository; - } - - /// - /// Maps to . - /// It uses by default. - /// It can be overriden for custom mapping. - /// - protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity) - { - return ObjectMapper.Map(entity); - } - - /// - /// Maps to . - /// It uses by default. - /// It can be overriden for custom mapping. - /// - protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity) - { - return ObjectMapper.Map(entity); - } - /// /// Maps to to create a new entity. /// It uses by default. @@ -284,18 +143,14 @@ namespace Volo.Abp.Application.Services /// protected virtual void SetIdForGuids(TEntity entity) { - var entityWithGuidId = entity as IEntity; - - if (entityWithGuidId == null || entityWithGuidId.Id != Guid.Empty) + if (entity is IEntity entityWithGuidId && entityWithGuidId.Id == Guid.Empty) { - return; + EntityHelper.TrySetId( + entityWithGuidId, + () => GuidGenerator.Create(), + true + ); } - - EntityHelper.TrySetId( - entityWithGuidId, - () => GuidGenerator.Create(), - true - ); } /// diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs new file mode 100644 index 0000000000..9085434211 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs @@ -0,0 +1,184 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.Application.Services +{ + public abstract class AbstractKeyReadOnlyAppService + : AbstractKeyReadOnlyAppService + where TEntity : class, IEntity + { + protected AbstractKeyReadOnlyAppService(IReadOnlyRepository repository) + : base(repository) + { + + } + } + + public abstract class AbstractKeyReadOnlyAppService + : AbstractKeyReadOnlyAppService + where TEntity : class, IEntity + { + protected AbstractKeyReadOnlyAppService(IReadOnlyRepository repository) + : base(repository) + { + + } + } + + public abstract class AbstractKeyReadOnlyAppService + : ApplicationService + , IReadOnlyAppService + where TEntity : class, IEntity + { + protected IReadOnlyRepository ReadOnlyRepository { get; } + + protected virtual string GetPolicyName { get; set; } + + protected virtual string GetListPolicyName { get; set; } + + protected AbstractKeyReadOnlyAppService(IReadOnlyRepository repository) + { + ReadOnlyRepository = repository; + } + + public virtual async Task GetAsync(TKey id) + { + await CheckGetPolicyAsync(); + + var entity = await GetEntityByIdAsync(id); + return MapToGetOutputDto(entity); + } + + public virtual async Task> GetListAsync(TGetListInput input) + { + await CheckGetListPolicyAsync(); + + var query = CreateFilteredQuery(input); + + var totalCount = await AsyncExecuter.CountAsync(query); + + query = ApplySorting(query, input); + query = ApplyPaging(query, input); + + var entities = await AsyncExecuter.ToListAsync(query); + + return new PagedResultDto( + totalCount, + entities.Select(MapToGetListOutputDto).ToList() + ); + } + + protected abstract Task GetEntityByIdAsync(TKey id); + + protected virtual async Task CheckGetPolicyAsync() + { + await CheckPolicyAsync(GetPolicyName); + } + + protected virtual async Task CheckGetListPolicyAsync() + { + await CheckPolicyAsync(GetListPolicyName); + } + + /// + /// Should apply sorting if needed. + /// + /// The query. + /// The input. + protected virtual IQueryable ApplySorting(IQueryable query, TGetListInput input) + { + //Try to sort query if available + if (input is ISortedResultRequest sortInput) + { + if (!sortInput.Sorting.IsNullOrWhiteSpace()) + { + return query.OrderBy(sortInput.Sorting); + } + } + + //IQueryable.Task requires sorting, so we should sort if Take will be used. + if (input is ILimitedResultRequest) + { + return ApplyDefaultSorting(query); + } + + //No sorting + return query; + } + + /// + /// Applies sorting if no sorting specified but a limited result requested. + /// + /// The query. + protected virtual IQueryable ApplyDefaultSorting(IQueryable query) + { + if (typeof(TEntity).IsAssignableTo()) + { + return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); + } + + throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyReadOnlyAppService!"); + } + + /// + /// Should apply paging if needed. + /// + /// The query. + /// The input. + protected virtual IQueryable ApplyPaging(IQueryable query, TGetListInput input) + { + //Try to use paging if available + if (input is IPagedResultRequest pagedInput) + { + return query.PageBy(pagedInput); + } + + //Try to limit query result if available + if (input is ILimitedResultRequest limitedInput) + { + return query.Take(limitedInput.MaxResultCount); + } + + //No paging + return query; + } + + /// + /// This method should create based on given input. + /// It should filter query if needed, but should not do sorting or paging. + /// Sorting should be done in and paging should be done in + /// methods. + /// + /// The input. + protected virtual IQueryable CreateFilteredQuery(TGetListInput input) + { + return ReadOnlyRepository; + } + + /// + /// Maps to . + /// It uses by default. + /// It can be overriden for custom mapping. + /// + protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity) + { + return ObjectMapper.Map(entity); + } + + /// + /// Maps to . + /// It uses by default. + /// It can be overriden for custom mapping. + /// + protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity) + { + return ObjectMapper.Map(entity); + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs index a2af0592a4..7832e8a4ab 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs @@ -70,7 +70,7 @@ namespace Volo.Abp.Application.Services protected new IRepository Repository { get; } protected CrudAppService(IRepository repository) - : base(repository) + : base(repository) { Repository = repository; } diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs new file mode 100644 index 0000000000..d99928126c --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.Application.Services +{ + public abstract class ReadOnlyAppService + : ReadOnlyAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected ReadOnlyAppService(IReadOnlyRepository repository) + : base(repository) + { + + } + } + + public abstract class ReadOnlyAppService + : ReadOnlyAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected ReadOnlyAppService(IReadOnlyRepository repository) + : base(repository) + { + + } + } + + public abstract class ReadOnlyAppService + : AbstractKeyReadOnlyAppService + where TEntity : class, IEntity + where TGetOutputDto : IEntityDto + where TGetListOutputDto : IEntityDto + { + protected new IReadOnlyRepository Repository { get; } + + protected ReadOnlyAppService(IReadOnlyRepository repository) + : base(repository) + { + Repository = repository; + } + + protected override async Task GetEntityByIdAsync(TKey id) + { + return await Repository.GetAsync(id); + } + + protected override IQueryable ApplyDefaultSorting(IQueryable query) + { + if (typeof(TEntity).IsAssignableTo()) + { + return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); + } + else + { + return query.OrderByDescending(e => e.Id); + } + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/ConcurrencyStampConsts.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/ConcurrencyStampConsts.cs new file mode 100644 index 0000000000..ac655a395d --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/ConcurrencyStampConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Domain.Entities +{ + public static class ConcurrencyStampConsts + { + public const int MaxLength = 40; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs index b78a5aa400..cf1b643305 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Entities { @@ -16,6 +14,11 @@ namespace Volo.Abp.Domain.Entities } public abstract object[] GetKeys(); + + public bool EntityEquals(IEntity other) + { + return EntityHelper.EntityEquals(this, other); + } } /// @@ -35,44 +38,6 @@ namespace Volo.Abp.Domain.Entities Id = id; } - public bool EntityEquals(object obj) - { - if (obj == null || !(obj is Entity)) - { - return false; - } - - //Same instances must be considered as equal - if (ReferenceEquals(this, obj)) - { - return true; - } - - //Transient objects are not considered as equal - var other = (Entity)obj; - if (EntityHelper.HasDefaultId(this) && EntityHelper.HasDefaultId(other)) - { - return false; - } - - //Must have a IS-A relation of types or must be same type - var typeOfThis = GetType().GetTypeInfo(); - var typeOfOther = other.GetType().GetTypeInfo(); - if (!typeOfThis.IsAssignableFrom(typeOfOther) && !typeOfOther.IsAssignableFrom(typeOfThis)) - { - return false; - } - - //Different tenants may have an entity with same Id. - if (this is IMultiTenant && other is IMultiTenant && - this.As().TenantId != other.As().TenantId) - { - return false; - } - - return Id.Equals(other.Id); - } - public override object[] GetKeys() { return new object[] {Id}; diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index ad082c3fb0..65f2992257 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Reflection; namespace Volo.Abp.Domain.Entities { @@ -16,6 +18,98 @@ namespace Volo.Abp.Domain.Entities private static readonly ConcurrentDictionary CachedIdProperties = new ConcurrentDictionary(); + public static bool EntityEquals(IEntity entity1, IEntity entity2) + { + if (entity1 == null || entity2 == null) + { + return false; + } + + //Same instances must be considered as equal + if (ReferenceEquals(entity1, entity2)) + { + return true; + } + + //Must have a IS-A relation of types or must be same type + var typeOfEntity1 = entity1.GetType(); + var typeOfEntity2 = entity2.GetType(); + if (!typeOfEntity1.IsAssignableFrom(typeOfEntity2) && !typeOfEntity2.IsAssignableFrom(typeOfEntity1)) + { + return false; + } + + //Different tenants may have an entity with same Id. + if (entity1 is IMultiTenant && entity2 is IMultiTenant) + { + var tenant1Id = ((IMultiTenant) entity1).TenantId; + var tenant2Id = ((IMultiTenant) entity2).TenantId; + + if (tenant1Id != tenant2Id) + { + if (tenant1Id == null || tenant2Id == null) + { + return false; + } + + if (!tenant1Id.Equals(tenant2Id)) + { + return false; + } + } + } + + //Transient objects are not considered as equal + if (HasDefaultKeys(entity1) && HasDefaultKeys(entity2)) + { + return false; + } + + var entity1Keys = entity1.GetKeys(); + var entity2Keys = entity2.GetKeys(); + + if (entity1Keys.Length != entity2Keys.Length) + { + return false; + } + + for (var i = 0; i < entity1Keys.Length; i++) + { + var entity1Key = entity1Keys[i]; + var entity2Key = entity2Keys[i]; + + if (entity1Key == null) + { + if (entity2Key == null) + { + //Both null, so considered as equals + continue; + } + + //entity2Key is not null! + return false; + } + + if (entity2Key == null) + { + //entity1Key was not null! + return false; + } + + if (TypeHelper.IsDefaultValue(entity1Key) && TypeHelper.IsDefaultValue(entity2Key)) + { + return false; + } + + if (!entity1Key.Equals(entity2Key)) + { + return false; + } + } + + return true; + } + public static bool IsEntity([NotNull] Type type) { return typeof(IEntity).IsAssignableFrom(type); @@ -25,7 +119,8 @@ namespace Volo.Abp.Domain.Entities { foreach (var interfaceType in type.GetInterfaces()) { - if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>)) + if (interfaceType.GetTypeInfo().IsGenericType && + interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>)) { return true; } @@ -55,6 +150,44 @@ namespace Volo.Abp.Domain.Entities return false; } + private static bool IsDefaultKeyValue(object value) + { + if (value == null) + { + return true; + } + + var type = value.GetType(); + + //Workaround for EF Core since it sets int/long to min value when attaching to DbContext + if (type == typeof(int)) + { + return Convert.ToInt32(value) <= 0; + } + + if (type == typeof(long)) + { + return Convert.ToInt64(value) <= 0; + } + + return TypeHelper.IsDefaultValue(value); + } + + public static bool HasDefaultKeys([NotNull] IEntity entity) + { + Check.NotNull(entity, nameof(entity)); + + foreach (var key in entity.GetKeys()) + { + if (!IsDefaultKeyValue(key)) + { + return false; + } + } + + return true; + } + /// /// Tries to find the primary key type of the given entity type. /// May return null if given type does not implement @@ -75,12 +208,14 @@ namespace Volo.Abp.Domain.Entities { if (!typeof(IEntity).IsAssignableFrom(entityType)) { - throw new AbpException($"Given {nameof(entityType)} is not an entity. It should implement {typeof(IEntity).AssemblyQualifiedName}!"); + throw new AbpException( + $"Given {nameof(entityType)} is not an entity. It should implement {typeof(IEntity).AssemblyQualifiedName}!"); } foreach (var interfaceType in entityType.GetTypeInfo().GetInterfaces()) { - if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>)) + if (interfaceType.GetTypeInfo().IsGenericType && + interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>)) { return interfaceType.GenericTypeArguments[0]; } @@ -132,4 +267,4 @@ namespace Volo.Abp.Domain.Entities property?.SetValue(entity, idFactory()); } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs index cad12b1450..5de3b22c77 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; @@ -18,6 +21,7 @@ namespace Volo.Abp.Domain.Entities.Events /// public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDependency { + public ILogger Logger { get; set; } public ILocalEventBus LocalEventBus { get; set; } public IDistributedEventBus DistributedEventBus { get; set; } @@ -36,6 +40,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus = NullLocalEventBus.Instance; DistributedEventBus = NullDistributedEventBus.Instance; + Logger = NullLogger.Instance; } public async Task TriggerEventsAsync(EntityChangeReport changeReport) @@ -56,6 +61,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityCreatingEventData<>), entity, + entity, true ); } @@ -66,6 +72,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityCreatedEventData<>), entity, + entity, false ); @@ -78,6 +85,7 @@ namespace Volo.Abp.Domain.Entities.Events DistributedEventBus, typeof(EntityCreatedEto<>), eto, + entity, false ); } @@ -101,6 +109,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityUpdatingEventData<>), entity, + entity, true ); } @@ -111,6 +120,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityUpdatedEventData<>), entity, + entity, false ); @@ -123,6 +133,7 @@ namespace Volo.Abp.Domain.Entities.Events DistributedEventBus, typeof(EntityUpdatedEto<>), eto, + entity, false ); } @@ -135,6 +146,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityDeletingEventData<>), entity, + entity, true ); } @@ -145,6 +157,7 @@ namespace Volo.Abp.Domain.Entities.Events LocalEventBus, typeof(EntityDeletedEventData<>), entity, + entity, false ); @@ -157,6 +170,7 @@ namespace Volo.Abp.Domain.Entities.Events DistributedEventBus, typeof(EntityDeletedEto<>), eto, + entity, false ); } @@ -206,22 +220,129 @@ namespace Volo.Abp.Domain.Entities.Events { foreach (var distributedEvent in distributedEvents) { - await DistributedEventBus.PublishAsync(distributedEvent.EventData.GetType(), distributedEvent.EventData); + await DistributedEventBus.PublishAsync(distributedEvent.EventData.GetType(), + distributedEvent.EventData); } } - protected virtual async Task TriggerEventWithEntity(IEventBus eventPublisher, Type genericEventType, object entity, bool triggerInCurrentUnitOfWork) + protected virtual async Task TriggerEventWithEntity( + IEventBus eventPublisher, + Type genericEventType, + object entityOrEto, + object originalEntity, + bool triggerInCurrentUnitOfWork) { - var entityType = ProxyHelper.UnProxy(entity).GetType(); + var entityType = ProxyHelper.UnProxy(entityOrEto).GetType(); var eventType = genericEventType.MakeGenericType(entityType); + var currentUow = UnitOfWorkManager.Current; - if (triggerInCurrentUnitOfWork || UnitOfWorkManager.Current == null) + if (triggerInCurrentUnitOfWork || currentUow == null) { - await eventPublisher.PublishAsync(eventType, Activator.CreateInstance(eventType, entity)); + await eventPublisher.PublishAsync( + eventType, + Activator.CreateInstance(eventType, entityOrEto) + ); + return; } - UnitOfWorkManager.Current.OnCompleted(() => eventPublisher.PublishAsync(eventType, Activator.CreateInstance(eventType, entity))); + var eventList = GetEventList(currentUow); + var isFirstEvent = !eventList.Any(); + + eventList.AddUniqueEvent(eventPublisher, eventType, entityOrEto, originalEntity); + + /* Register to OnCompleted if this is the first item. + * Other items will already be in the list once the UOW completes. + */ + if (isFirstEvent) + { + currentUow.OnCompleted( + async () => + { + foreach (var eventEntry in eventList) + { + try + { + await eventPublisher.PublishAsync( + eventEntry.EventType, + Activator.CreateInstance(eventEntry.EventType, eventEntry.EntityOrEto) + ); + } + catch (Exception ex) + { + Logger.LogError( + $"Caught an exception while publishing the event '{eventType.FullName}' for the entity '{entityOrEto}'"); + Logger.LogException(ex); + } + } + } + ); + } + } + + private EntityChangeEventList GetEventList(IUnitOfWork currentUow) + { + return (EntityChangeEventList) currentUow.Items.GetOrAdd( + "AbpEntityChangeEventList", + () => new EntityChangeEventList() + ); + } + + private class EntityChangeEventList : List + { + public void AddUniqueEvent(IEventBus eventBus, Type eventType, object entityOrEto, object originalEntity) + { + var newEntry = new EntityChangeEventEntry(eventBus, eventType, entityOrEto, originalEntity); + + //Latest "same" event overrides the previous events. + for (var i = 0; i < Count; i++) + { + if (this[i].IsSameEvent(newEntry)) + { + this[i] = newEntry; + return; + } + } + + //If this is a "new" event, add to the end + Add(newEntry); + } + } + + private class EntityChangeEventEntry + { + public IEventBus EventBus { get; } + + public Type EventType { get; } + + public object EntityOrEto { get; } + + public object OriginalEntity { get; } + + public EntityChangeEventEntry(IEventBus eventBus, Type eventType, object entityOrEto, object originalEntity) + { + EventType = eventType; + EntityOrEto = entityOrEto; + OriginalEntity = originalEntity; + EventBus = eventBus; + } + + public bool IsSameEvent(EntityChangeEventEntry otherEntry) + { + if (EventBus != otherEntry.EventBus || EventType != otherEntry.EventType) + { + return false; + } + + var originalEntityRef = OriginalEntity as IEntity; + var otherOriginalEntityRef = otherEntry.OriginalEntity as IEntity; + if (originalEntityRef == null || otherOriginalEntityRef == null) + { + return false; + } + + return EntityHelper.EntityEquals(originalEntityRef, otherOriginalEntityRef); + } } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj index c4763c96ca..8043a1ff8e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xml b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xsd b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj new file mode 100644 index 0000000000..6196a5b682 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.1 + Volo.Abp.EntityFrameworkCore.Oracle + Volo.Abp.EntityFrameworkCore.Oracle + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleExtensions.cs new file mode 100644 index 0000000000..a5b898b286 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleExtensions.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using System; +using Oracle.EntityFrameworkCore.Infrastructure; +using Volo.Abp.EntityFrameworkCore.DependencyInjection; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextConfigurationContextOracleExtensions + { + public static DbContextOptionsBuilder UseOracle( + [NotNull] this AbpDbContextConfigurationContext context, + [CanBeNull] Action oracleOptionsAction = null) + { + if (context.ExistingConnection != null) + { + return context.DbContextOptions.UseOracle(context.ExistingConnection, oracleOptionsAction); + } + else + { + return context.DbContextOptions.UseOracle(context.ConnectionString, oracleOptionsAction); + } + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleExtensions.cs new file mode 100644 index 0000000000..f4817d5ab4 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleExtensions.cs @@ -0,0 +1,30 @@ +using JetBrains.Annotations; +using System; +using Oracle.EntityFrameworkCore.Infrastructure; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextOptionsOracleExtensions + { + public static void UseOracle( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action oracleOptionsAction = null) + { + options.Configure(context => + { + context.UseOracle(oracleOptionsAction); + }); + } + + public static void UseOracle( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action oracleOptionsAction = null) + where TDbContext : AbpDbContext + { + options.Configure(context => + { + context.UseOracle(oracleOptionsAction); + }); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs new file mode 100644 index 0000000000..3b7c801462 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/Oracle/AbpEntityFrameworkCoreOracleModule.cs @@ -0,0 +1,22 @@ +using Volo.Abp.Guids; +using Volo.Abp.Modularity; + +namespace Volo.Abp.EntityFrameworkCore.Oracle +{ + [DependsOn( + typeof(AbpEntityFrameworkCoreModule) + )] + public class AbpEntityFrameworkCoreOracleModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + if (options.DefaultSequentialGuidType == null) + { + options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary; + } + }); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj index ced9c624ad..96de9fdd1c 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj index 309e53274f..988c3688ee 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj index 9f4f26c45b..ce5c9a1716 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj index 3ce20b3f4e..da97fb5977 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index fb2d2dfb1f..92ac4a8d8c 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -129,6 +129,7 @@ namespace Volo.Abp.EntityFrameworkCore return EfCoreDatabaseProvider.PostgreSql; case "Pomelo.EntityFrameworkCore.MySql": return EfCoreDatabaseProvider.MySql; + case "Oracle.EntityFrameworkCore": case "Devart.Data.Oracle.Entity.EFCore": return EfCoreDatabaseProvider.Oracle; case "Microsoft.EntityFrameworkCore.Sqlite": diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs index 8db091eb30..796c6e20fb 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; @@ -241,6 +242,11 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory } } + if (IsBaseAuditProperty(propertyInfo, entityType)) + { + return false; + } + var isModified = !(propertyEntry.OriginalValue?.Equals(propertyEntry.CurrentValue) ?? propertyEntry.CurrentValue == null); if (isModified) { @@ -250,6 +256,59 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory return defaultValue; } + private bool IsBaseAuditProperty(PropertyInfo propertyInfo, Type entityType) + { + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasCreationTime.CreationTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IMayHaveCreator.CreatorId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IMustHaveCreator.CreatorId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasModificationTime.LastModificationTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IModificationAuditedObject.LastModifierId)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(ISoftDelete.IsDeleted)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IHasDeletionTime.DeletionTime)) + { + return true; + } + + if (entityType.IsAssignableTo() + && propertyInfo.Name == nameof(IDeletionAuditedObject.DeleterId)) + { + return true; + } + + return false; + } + /// /// Updates change time, entity id and foreign keys after SaveChanges is called. /// @@ -313,4 +372,4 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory } } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index 0a84b6871d..44962eca4c 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -40,9 +40,9 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling { if (b.Metadata.ClrType.IsAssignableTo()) { - //TODO: Max length? b.Property(nameof(IHasConcurrencyStamp.ConcurrencyStamp)) .IsConcurrencyToken() + .HasMaxLength(ConcurrencyStampConsts.MaxLength) .HasColumnName(nameof(IHasConcurrencyStamp.ConcurrencyStamp)); } } diff --git a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj index 52cee1ade7..68ce50ec9d 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj +++ b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json deleted file mode 100644 index d56e5c65b7..0000000000 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52306/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client.IdentityModel": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52307/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json deleted file mode 100644 index d56e5c65b7..0000000000 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52306/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client.IdentityModel": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52307/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json b/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json deleted file mode 100644 index 3cef3cf668..0000000000 --- a/framework/src/Volo.Abp.Http.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:52208/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Http.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:52209/" - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj index 8607f32a35..4441e0bc17 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj +++ b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj b/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj index 6c19f18397..0143db8265 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj +++ b/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj b/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj index 3f75ccba80..ddded71dce 100644 --- a/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj +++ b/framework/src/Volo.Abp.MailKit/Volo.Abp.MailKit.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs index 2457c3c453..1dc38bf5ea 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs @@ -4,7 +4,12 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Local; +using Volo.Abp.Guids; using Volo.Abp.MemoryDb; namespace Volo.Abp.Domain.Repositories.MemoryDb @@ -20,62 +25,224 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual IMemoryDatabase Database => DatabaseProvider.GetDatabase(); protected IMemoryDatabaseProvider DatabaseProvider { get; } + + public ILocalEventBus LocalEventBus { get; set; } + + public IDistributedEventBus DistributedEventBus { get; set; } + + public IEntityChangeEventHelper EntityChangeEventHelper { get; set; } + + public IAuditPropertySetter AuditPropertySetter { get; set; } + + public IGuidGenerator GuidGenerator { get; set; } public MemoryDbRepository(IMemoryDatabaseProvider databaseProvider) { DatabaseProvider = databaseProvider; + + LocalEventBus = NullLocalEventBus.Instance; + DistributedEventBus = NullDistributedEventBus.Instance; + EntityChangeEventHelper = NullEntityChangeEventHelper.Instance; } protected override IQueryable GetQueryable() { return ApplyDataFilters(Collection.AsQueryable()); } + + protected virtual async Task TriggerDomainEventsAsync(object entity) + { + var generatesDomainEventsEntity = entity as IGeneratesDomainEvents; + if (generatesDomainEventsEntity == null) + { + return; + } + + var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray(); + if (localEvents != null && localEvents.Any()) + { + foreach (var localEvent in localEvents) + { + await LocalEventBus.PublishAsync(localEvent.GetType(), localEvent); + } + + generatesDomainEventsEntity.ClearLocalEvents(); + } + + var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray(); + if (distributedEvents != null && distributedEvents.Any()) + { + foreach (var distributedEvent in distributedEvents) + { + await DistributedEventBus.PublishAsync(distributedEvent.GetType(), distributedEvent); + } + + generatesDomainEventsEntity.ClearDistributedEvents(); + } + } + + protected virtual bool IsHardDeleted(TEntity entity) + { + if (!(UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) is HashSet hardDeletedEntities)) + { + return false; + } + + return hardDeletedEntities.Contains(entity); + } + + protected virtual void CheckAndSetId(TEntity entity) + { + if (entity is IEntity entityWithGuidId) + { + TrySetGuidId(entityWithGuidId); + } + } + + protected virtual void TrySetGuidId(IEntity entity) + { + if (entity.Id != default) + { + return; + } + + EntityHelper.TrySetId( + entity, + () => GuidGenerator.Create(), + true + ); + } + + protected virtual void SetCreationAuditProperties(TEntity entity) + { + AuditPropertySetter.SetCreationProperties(entity); + } + + protected virtual void SetModificationAuditProperties(TEntity entity) + { + AuditPropertySetter.SetModificationProperties(entity); + } + + protected virtual void SetDeletionAuditProperties(TEntity entity) + { + AuditPropertySetter.SetDeletionProperties(entity); + } + + protected virtual async Task TriggerEntityCreateEvents(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityCreatedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityCreatingEventAsync(entity); + } + + protected virtual async Task TriggerEntityUpdateEventsAsync(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityUpdatingEventAsync(entity); + } + + protected virtual async Task TriggerEntityDeleteEventsAsync(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityDeletingEventAsync(entity); + } + + protected virtual async Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity) + { + CheckAndSetId(entity); + SetCreationAuditProperties(entity); + await TriggerEntityCreateEvents(entity); + await TriggerDomainEventsAsync(entity); + } + + protected virtual async Task ApplyAbpConceptsForDeletedEntityAsync(TEntity entity) + { + SetDeletionAuditProperties(entity); + await TriggerEntityDeleteEventsAsync(entity); + await TriggerDomainEventsAsync(entity); + } public override Task FindAsync( Expression> predicate, bool includeDetails = true, CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.AsQueryable().Where(predicate).SingleOrDefault()); + return Task.FromResult(GetQueryable().Where(predicate).SingleOrDefault()); } - public override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default) + public override async Task DeleteAsync( + Expression> predicate, + bool autoSave = false, + CancellationToken cancellationToken = default) { - var entities = Collection.AsQueryable().Where(predicate).ToList(); + var entities = GetQueryable().Where(predicate).ToList(); foreach (var entity in entities) { - Collection.Remove(entity); + await DeleteAsync(entity, autoSave, cancellationToken); } - - return Task.CompletedTask; } - public override Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + public override async Task InsertAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { + await ApplyAbpConceptsForAddedEntityAsync(entity); + Collection.Add(entity); - return Task.FromResult(entity); + + return entity; } - - public override Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + + public override async Task UpdateAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { + SetModificationAuditProperties(entity); + + if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted) + { + SetDeletionAuditProperties(entity); + await TriggerEntityDeleteEventsAsync(entity); + } + else + { + await TriggerEntityUpdateEventsAsync(entity); + } + + await TriggerDomainEventsAsync(entity); + Collection.Update(entity); - return Task.FromResult(entity); - } - public override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + return entity; + } + + public override async Task DeleteAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { - Collection.Remove(entity); - return Task.CompletedTask; + await ApplyAbpConceptsForDeletedEntityAsync(entity); + + if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity)) + { + softDeleteEntity.IsDeleted = true; + Collection.Update(entity); + } + else + { + Collection.Remove(entity); + } } public override Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.ToList()); + return Task.FromResult(GetQueryable().ToList()); } public override Task GetCountAsync(CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.LongCount()); + return Task.FromResult(GetQueryable().LongCount()); } } @@ -126,13 +293,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) { - var entity = await FindAsync(id, cancellationToken: cancellationToken); - if (entity == null) - { - return; - } - - await DeleteAsync(entity, autoSave, cancellationToken); + await DeleteAsync(x => x.Id.Equals(id), autoSave, cancellationToken); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Minify/Volo.Abp.Minify.csproj b/framework/src/Volo.Abp.Minify/Volo.Abp.Minify.csproj index ee2ecc23e6..1b2f266637 100644 --- a/framework/src/Volo.Abp.Minify/Volo.Abp.Minify.csproj +++ b/framework/src/Volo.Abp.Minify/Volo.Abp.Minify.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj b/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj index 7e14eb9a9e..2b9e46f43d 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj +++ b/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs new file mode 100644 index 0000000000..1790c8af6b --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using MongoDB.Driver; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Domain.Repositories.MongoDB +{ + public interface IMongoDbRepositoryFilterer where TEntity : class, IEntity + { + void AddGlobalFilters(List> filters); + } + + public interface IMongoDbRepositoryFilterer : IMongoDbRepositoryFilterer where TEntity : class, IEntity + { + FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false); + + FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index 3ac6ab5239..e8477f20e9 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -14,7 +14,6 @@ using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.MongoDB; -using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Repositories.MongoDB { @@ -331,6 +330,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB where TMongoDbContext : IAbpMongoDbContext where TEntity : class, IEntity { + public IMongoDbRepositoryFilterer RepositoryFilterer { get; set; } + public MongoDbRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { @@ -358,7 +359,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB CancellationToken cancellationToken = default) { return await Collection - .Find(CreateEntityFilter(id, true)) + .Find(RepositoryFilterer.CreateEntityFilter(id, true)) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } @@ -372,49 +373,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB protected override FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) { - if (!withConcurrencyStamp || !(entity is IHasConcurrencyStamp entityWithConcurrencyStamp)) - { - return Builders.Filter.Eq(e => e.Id, entity.Id); - } - - if (concurrencyStamp == null) - { - concurrencyStamp = entityWithConcurrencyStamp.ConcurrencyStamp; - } - - return Builders.Filter.And( - Builders.Filter.Eq(e => e.Id, entity.Id), - Builders.Filter.Eq(e => ((IHasConcurrencyStamp)e).ConcurrencyStamp, concurrencyStamp) - ); - } - - protected virtual FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false) - { - var filters = new List> - { - Builders.Filter.Eq(e => e.Id, id) - }; - - if (applyFilters) - { - AddGlobalFilters(filters); - } - - return Builders.Filter.And(filters); - } - - protected virtual void AddGlobalFilters(List> filters) - { - if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) - { - filters.Add(Builders.Filter.Eq(e => ((ISoftDelete)e).IsDeleted, false)); - } - - if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) - { - var tenantId = CurrentTenant.Id; - filters.Add(Builders.Filter.Eq(e => ((IMultiTenant)e).TenantId, tenantId)); - } + return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs new file mode 100644 index 0000000000..4c07c747f3 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Domain.Repositories.MongoDB +{ + public class MongoDbRepositoryFilterer : IMongoDbRepositoryFilterer + where TEntity : class, IEntity + { + protected IDataFilter DataFilter { get; } + + protected ICurrentTenant CurrentTenant { get; } + + public MongoDbRepositoryFilterer(IDataFilter dataFilter, ICurrentTenant currentTenant) + { + DataFilter = dataFilter; + CurrentTenant = currentTenant; + } + + public virtual void AddGlobalFilters(List> filters) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) + { + filters.Add(Builders.Filter.Eq(e => ((ISoftDelete) e).IsDeleted, false)); + } + + if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) + { + var tenantId = CurrentTenant.Id; + filters.Add(Builders.Filter.Eq(e => ((IMultiTenant) e).TenantId, tenantId)); + } + } + } + + public class MongoDbRepositoryFilterer : MongoDbRepositoryFilterer, + IMongoDbRepositoryFilterer + where TEntity : class, IEntity + { + public MongoDbRepositoryFilterer(IDataFilter dataFilter, ICurrentTenant currentTenant) + : base(dataFilter, currentTenant) + { + } + + public FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false) + { + var filters = new List> + { + Builders.Filter.Eq(e => e.Id, id) + }; + + if (applyFilters) + { + AddGlobalFilters(filters); + } + + return Builders.Filter.And(filters); + } + + public FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) + { + if (!withConcurrencyStamp || !(entity is IHasConcurrencyStamp entityWithConcurrencyStamp)) + { + return Builders.Filter.Eq(e => e.Id, entity.Id); + } + + if (concurrencyStamp == null) + { + concurrencyStamp = entityWithConcurrencyStamp.ConcurrencyStamp; + } + + return Builders.Filter.And( + Builders.Filter.Eq(e => e.Id, entity.Id), + Builders.Filter.Eq(e => ((IHasConcurrencyStamp) e).ConcurrencyStamp, concurrencyStamp) + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs index d3e8055a22..e36e972954 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Domain; +using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Modularity; using Volo.Abp.Uow.MongoDB; @@ -14,6 +15,16 @@ namespace Volo.Abp.MongoDB typeof(IMongoDbContextProvider<>), typeof(UnitOfWorkMongoDbContextProvider<>) ); + + context.Services.TryAddTransient( + typeof(IMongoDbRepositoryFilterer<>), + typeof(MongoDbRepositoryFilterer<>) + ); + + context.Services.TryAddTransient( + typeof(IMongoDbRepositoryFilterer<,>), + typeof(MongoDbRepositoryFilterer<,>) + ); } } } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs index 1ba519680c..a637351e66 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.Data { - //TODO: Move to Volo.Abp.Data.ObjectExtending namespace at v3.0 + //TODO: Move to Volo.Abp.Data.ObjectExtending namespace at 4.0? public interface IHasExtraProperties { diff --git a/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj index 7bcc0400b5..41bba98866 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj +++ b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json index ae50048d03..496e6e3e9d 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json @@ -13,18 +13,22 @@ "DefaultErrorMessage404": "未找到資源!", "DefaultErrorMessage404Detail": "未在服務中找到請求的資源!", "EntityNotFoundErrorMessage": "實體 {0} 不存在,id = {1}!", + "Languages": "語系", "Error": "錯誤", "AreYouSure": "你確定嗎?", "Cancel": "取消", "Yes": "是", "No": "否", + "Ok": "好", "Close": "關閉", "Save": "保存", "SavingWithThreeDot": "保存中...", "Actions": "操作", "Delete": "刪除", "Edit": "修改", - "Refresh": "刷新", + "Refresh": "更新", + "Language": "語言", + "LoadMore": "讀更多", "ProcessingWithThreeDot": "處理中...", "LoadingWithThreeDot": "載入中...", "Welcome": "歡迎", @@ -43,11 +47,13 @@ "PagerInfoEmpty": "顯示0個紀錄中的0到0", "PagerInfoFiltered": "(從 _MAX_ 所有紀錄中過濾掉)", "NoDataAvailableInDatatable": "資料表中沒有資料", + "Total": "總共", + "Selected": "已選擇", "PagerShowMenuEntries": "顯示 _MENU_ 實體", "DatatableActionDropdownDefaultText": "操作", "ChangePassword": "修改密碼", "PersonalInfo": "個人資料", - "AreYouSureYouWantToCancelEditingWarningMessage": "你有未保存的更改.", + "AreYouSureYouWantToCancelEditingWarningMessage": "你有未保存的更改", "UnhandledException": "未處理的異常!", "401Message": "未授權", "403Message": "禁止訪問", @@ -55,6 +61,6 @@ "500Message": "內部伺服器錯誤", "GoHomePage": "返回首頁", "GoBack": "返回", - "Search": "搜索" + "Search": "查詢" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj b/framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj index 46995e4eb4..b4aa55b1c0 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj @@ -15,8 +15,9 @@ - - + + + diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/AbpVirtualFileSystemOptions.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/AbpVirtualFileSystemOptions.cs index c4a4d203d2..aff1aee4e9 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/AbpVirtualFileSystemOptions.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/AbpVirtualFileSystemOptions.cs @@ -3,7 +3,7 @@ public class AbpVirtualFileSystemOptions { public VirtualFileSetList FileSets { get; } - + public AbpVirtualFileSystemOptions() { FileSets = new VirtualFileSetList(); diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedFileSet.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/AbpEmbeddedFileProvider.cs similarity index 81% rename from framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedFileSet.cs rename to framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/AbpEmbeddedFileProvider.cs index d2a0556527..ae0acafe09 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedFileSet.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/AbpEmbeddedFileProvider.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.FileProviders; namespace Volo.Abp.VirtualFileSystem.Embedded { - public class EmbeddedFileSet : IVirtualFileSet + public class AbpEmbeddedFileProvider : DictionaryBasedFileProvider { [NotNull] public Assembly Assembly { get; } @@ -16,19 +16,22 @@ namespace Volo.Abp.VirtualFileSystem.Embedded [CanBeNull] public string BaseNamespace { get; } - [CanBeNull] - public string BaseFolderInProject { get; } + protected override IDictionary Files => _files.Value; + private readonly Lazy> _files; - public EmbeddedFileSet( + public AbpEmbeddedFileProvider( [NotNull] Assembly assembly, - [CanBeNull] string baseNamespace = null, - [CanBeNull] string baseFolderInProject = null) + [CanBeNull] string baseNamespace = null) { Check.NotNull(assembly, nameof(assembly)); Assembly = assembly; BaseNamespace = baseNamespace; - BaseFolderInProject = baseFolderInProject; + + _files = new Lazy>( + CreateFiles, + true + ); } public void AddFiles(Dictionary files) @@ -127,5 +130,17 @@ namespace Volo.Abp.VirtualFileSystem.Embedded return filePath.Substring(filePath.LastIndexOf("/", StringComparison.Ordinal) + 1); } + + protected override string NormalizePath(string subpath) + { + return VirtualFilePathHelper.NormalizePath(subpath); + } + + private Dictionary CreateFiles() + { + var files = new Dictionary(StringComparer.OrdinalIgnoreCase); + AddFiles(files); + return files; + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedVirtualFileSetInfo.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedVirtualFileSetInfo.cs new file mode 100644 index 0000000000..9f4d87d783 --- /dev/null +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedVirtualFileSetInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using Microsoft.Extensions.FileProviders; + +namespace Volo.Abp.VirtualFileSystem.Embedded +{ + public class EmbeddedVirtualFileSetInfo : VirtualFileSetInfo + { + public Assembly Assembly { get; } + + public string BaseFolder { get; } + + public EmbeddedVirtualFileSetInfo( + IFileProvider fileProvider, + Assembly assembly, + string baseFolder = null) + : base(fileProvider) + { + Assembly = assembly; + BaseFolder = baseFolder; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/IVirtualFileSet.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/IVirtualFileSet.cs deleted file mode 100644 index 1880165d02..0000000000 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/IVirtualFileSet.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using Microsoft.Extensions.FileProviders; - -namespace Volo.Abp.VirtualFileSystem -{ - public interface IVirtualFileSet - { - void AddFiles(Dictionary files); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Physical/PhysicalVirtualFileSetInfo.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Physical/PhysicalVirtualFileSetInfo.cs new file mode 100644 index 0000000000..525fa178d6 --- /dev/null +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Physical/PhysicalVirtualFileSetInfo.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.FileProviders; + +namespace Volo.Abp.VirtualFileSystem.Physical +{ + public class PhysicalVirtualFileSetInfo : VirtualFileSetInfo + { + public string Root { get; } + + public PhysicalVirtualFileSetInfo( + [NotNull] IFileProvider fileProvider, + [NotNull] string root + ) + : base(fileProvider) + { + Root = Check.NotNullOrWhiteSpace(root, nameof(root)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFilePathHelper.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFilePathHelper.cs index 03f851a7e3..47900d5aa9 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFilePathHelper.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFilePathHelper.cs @@ -6,8 +6,6 @@ namespace Volo.Abp.VirtualFileSystem { internal static class VirtualFilePathHelper { - //TODO: Optimize this class! - public static string NormalizePath(string fullPath) { if (fullPath.Equals("/", StringComparison.Ordinal)) diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs index 5f90ef7925..eb4cd4e415 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; @@ -28,6 +27,11 @@ namespace Volo.Abp.VirtualFileSystem public virtual IDirectoryContents GetDirectoryContents(string subpath) { + if (subpath == "") + { + subpath = "/"; + } + return _hybridFileProvider.GetDirectoryContents(subpath); } @@ -42,52 +46,12 @@ namespace Volo.Abp.VirtualFileSystem fileProviders.Add(dynamicFileProvider); - if (_options.FileSets.PhysicalPaths.Any()) + foreach (var fileSet in _options.FileSets.AsEnumerable().Reverse()) { - fileProviders.AddRange( - _options.FileSets.PhysicalPaths - .Select(rootPath => new PhysicalFileProvider(rootPath)) - .Reverse() - ); + fileProviders.Add(fileSet.FileProvider); } - fileProviders.Add(new InternalVirtualFileProvider(_options)); - return new CompositeFileProvider(fileProviders); } - - protected class InternalVirtualFileProvider : DictionaryBasedFileProvider - { - protected override IDictionary Files => _files.Value; - - private readonly AbpVirtualFileSystemOptions _options; - private readonly Lazy> _files; - - public InternalVirtualFileProvider(AbpVirtualFileSystemOptions options) - { - _options = options; - _files = new Lazy>( - CreateFiles, - true - ); - } - - private Dictionary CreateFiles() - { - var files = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var set in _options.FileSets) - { - set.AddFiles(files); - } - - return files; - } - - protected override string NormalizePath(string subpath) - { - return VirtualFilePathHelper.NormalizePath(subpath); - } - } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetInfo.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetInfo.cs new file mode 100644 index 0000000000..694729f02a --- /dev/null +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetInfo.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.FileProviders; + +namespace Volo.Abp.VirtualFileSystem +{ + public class VirtualFileSetInfo + { + public IFileProvider FileProvider { get; } + + public VirtualFileSetInfo([NotNull] IFileProvider fileProvider) + { + FileProvider = Check.NotNull(fileProvider, nameof(fileProvider)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs index 2ddd0ff3a4..371f8350d8 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs @@ -2,13 +2,8 @@ namespace Volo.Abp.VirtualFileSystem { - public class VirtualFileSetList : List + public class VirtualFileSetList : List { - public List PhysicalPaths { get; } - - public VirtualFileSetList() - { - PhysicalPaths = new List(); - } + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs index 399f76f408..f1ca9749ea 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs @@ -1,44 +1,90 @@ using System; using System.IO; -using System.Linq; +using System.Reflection; using JetBrains.Annotations; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.FileProviders.Physical; using Volo.Abp.VirtualFileSystem.Embedded; +using Volo.Abp.VirtualFileSystem.Physical; namespace Volo.Abp.VirtualFileSystem { public static class VirtualFileSetListExtensions { - public static void AddEmbedded([NotNull] this VirtualFileSetList list, [CanBeNull] string baseNamespace = null, string baseFolderInProject = null) + public static void AddEmbedded( + [NotNull] this VirtualFileSetList list, + [CanBeNull] string baseNamespace = null, + [CanBeNull] string baseFolder = null) { Check.NotNull(list, nameof(list)); - list.Add( - new EmbeddedFileSet( - typeof(T).Assembly, - baseNamespace, - baseFolderInProject - ) + var assembly = typeof(T).Assembly; + var fileProvider = CreateFileProvider( + assembly, + baseNamespace, + baseFolder ); + + list.Add(new EmbeddedVirtualFileSetInfo(fileProvider, assembly, baseFolder)); } - public static void ReplaceEmbeddedByPhysical([NotNull] this VirtualFileSetList list, [NotNull] string pyhsicalPath) + public static void AddPhysical( + [NotNull] this VirtualFileSetList list, + [NotNull] string root, + ExclusionFilters exclusionFilters = ExclusionFilters.Sensitive) { Check.NotNull(list, nameof(list)); - Check.NotNull(pyhsicalPath, nameof(pyhsicalPath)); + Check.NotNullOrWhiteSpace(root, nameof(root)); - var assembly = typeof(T).Assembly; - var embeddedFileSets = list.OfType().Where(fs => fs.Assembly == assembly).ToList(); + var fileProvider = new PhysicalFileProvider(root, exclusionFilters); + list.Add(new PhysicalVirtualFileSetInfo(fileProvider, root)); + } + + private static IFileProvider CreateFileProvider( + [NotNull] Assembly assembly, + [CanBeNull] string baseNamespace = null, + [CanBeNull] string baseFolder = null) + { + Check.NotNull(assembly, nameof(assembly)); - foreach (var embeddedFileSet in embeddedFileSets) + var info = assembly.GetManifestResourceInfo("Microsoft.Extensions.FileProviders.Embedded.Manifest.xml"); + + if (info == null) { - list.Remove(embeddedFileSet); + return new AbpEmbeddedFileProvider(assembly, baseNamespace); + } + + if (baseFolder == null) + { + return new ManifestEmbeddedFileProvider(assembly); + } - if (!embeddedFileSet.BaseFolderInProject.IsNullOrEmpty()) + return new ManifestEmbeddedFileProvider(assembly, baseFolder); + } + + public static void ReplaceEmbeddedByPhysical( + [NotNull] this VirtualFileSetList fileSets, + [NotNull] string physicalPath) + { + Check.NotNull(fileSets, nameof(fileSets)); + Check.NotNullOrWhiteSpace(physicalPath, nameof(physicalPath)); + + var assembly = typeof(T).Assembly; + + for (var i = 0; i < fileSets.Count; i++) + { + if (fileSets[i] is EmbeddedVirtualFileSetInfo embeddedVirtualFileSet && + embeddedVirtualFileSet.Assembly == assembly) { - pyhsicalPath = Path.Combine(pyhsicalPath, embeddedFileSet.BaseFolderInProject); - } + var thisPath = physicalPath; - list.PhysicalPaths.Add(pyhsicalPath); + if (!embeddedVirtualFileSet.BaseFolder.IsNullOrEmpty()) + { + thisPath = Path.Combine(thisPath, embeddedVirtualFileSet.BaseFolder); + } + + fileSets[i] = new PhysicalVirtualFileSetInfo(new PhysicalFileProvider(thisPath), thisPath); + } } } } diff --git a/framework/test/AbpTestBase/AbpTestBase.csproj b/framework/test/AbpTestBase/AbpTestBase.csproj index e1a625580d..a1b2dcd89b 100644 --- a/framework/test/AbpTestBase/AbpTestBase.csproj +++ b/framework/test/AbpTestBase/AbpTestBase.csproj @@ -14,12 +14,12 @@ - - + + - + diff --git a/framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj b/framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj index 6cd2465068..49c80c56db 100644 --- a/framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj +++ b/framework/test/SimpleConsoleDemo/SimpleConsoleDemo.csproj @@ -18,7 +18,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj index e1253f04c3..0d170148d4 100644 --- a/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj index a24f7796c9..9e7bf68457 100644 --- a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index bb841bc085..b3842f7271 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs index 632a02194f..f2ab8fd3d0 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ModelBinding/ModelBindingController_Tests.cs @@ -13,11 +13,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding { protected DateTimeKind DateTimeKind { get; set; } - protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - services.Configure(x => x.Kind = DateTimeKind); - } - [Fact] public async Task DateTimeKind_Test() { @@ -84,17 +79,19 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding public class ModelBindingController_Utc_Tests : ModelBindingController_Tests { - public ModelBindingController_Utc_Tests() + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) { DateTimeKind = DateTimeKind.Utc; + services.Configure(x => x.Kind = DateTimeKind); } } public class ModelBindingController_Local_Tests : ModelBindingController_Tests { - public ModelBindingController_Local_Tests() + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) { DateTimeKind = DateTimeKind.Local; + services.Configure(x => x.Kind = DateTimeKind); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs index 57cb40219b..6ce47c4679 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Shouldly; +using Volo.Abp.Validation; namespace Volo.Abp.AspNetCore.Mvc.Validation { @@ -31,6 +32,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation { return Content("ModelState.IsValid: " + ModelState.IsValid.ToString().ToLowerInvariant()); } + + [HttpGet] + [Route("object-result-action-dynamic-length")] + public Task ObjectResultActionDynamicLength(ValidationDynamicTestModel model) + { + ModelState.IsValid.ShouldBeTrue(); //AbpValidationFilter throws exception otherwise + return Task.FromResult(model.Value1); + } public class ValidationTest1Model { @@ -38,6 +47,28 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation [StringLength(5, MinimumLength = 2)] public string Value1 { get; set; } } + + public class ValidationDynamicTestModel + { + [DynamicStringLength(typeof(Consts), nameof(Consts.MaxValue1Length), nameof(Consts.MinValue1Length))] + public string Value1 { get; set; } + + [DynamicMaxLength(typeof(Consts), nameof(Consts.MaxValue2Length))] + public string Value2 { get; set; } + + [DynamicMaxLength(typeof(Consts), nameof(Consts.MaxValue3Length))] + public int[] Value3 { get; set; } + + public static class Consts + { + public static int MinValue1Length { get; set; } = 2; + public static int MaxValue1Length { get; set; } = 7; + + public static int MaxValue2Length { get; set; } = 4; + + public static int MaxValue3Length { get; set; } = 2; + } + } public class CustomValidateModel : IValidatableObject { @@ -51,6 +82,5 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation } } } - } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs index 99e027f85a..9e22784ba7 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs @@ -58,5 +58,28 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation result.Error.ValidationErrors.ShouldContain(x => x.Message == "Value1 should be hello"); } + [Fact] + public async Task Should_Validate_Dynamic_Length_Object_Result_Success() + { + var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action-dynamic-length?value1=hello&value3[0]=53&value3[1]=54"); + result.ShouldBe("hello"); + + } + + [Fact] + public async Task Should_Validate_Dynamic_Length_Object_Result_Failing() + { + var result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=a", HttpStatusCode.BadRequest); //value1 has min string length of 2 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=12345678", HttpStatusCode.BadRequest); //value1 has max string length of 7 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=123458&value2=12345", HttpStatusCode.BadRequest); //value2 has max length of 5 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=123458&value3[0]=53&value3[1]=54&value3[2]=55&value3[3]=56", HttpStatusCode.BadRequest); //value3 has max length of 2. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj index 53e92dc13a..f1fa33a3b3 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj index f0301e04fe..ccbffb47f4 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj @@ -8,7 +8,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj index fb8b7abd07..92e5e21867 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj index 7a95c77246..4c328a670f 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj index 1d3fbf892f..e519e733de 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj index 02cf2264a4..77c291f0a0 100644 --- a/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Serilog.Tests/Volo.Abp.AspNetCore.Serilog.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj index 8e1ab29329..3bf5a6badd 100644 --- a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj index 8dc3153efb..5becdbc672 100644 --- a/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj index 3c11330181..2b74dd0a58 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs new file mode 100644 index 0000000000..9ff57b286d --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndHasCustomAuditingProperties.cs @@ -0,0 +1,26 @@ +using System; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Auditing.App.Entities +{ + [Audited] + public class AppEntityWithAuditedAndHasCustomAuditingProperties : AggregateRoot + { + protected AppEntityWithAuditedAndHasCustomAuditingProperties() + { + } + + public AppEntityWithAuditedAndHasCustomAuditingProperties(Guid id) + : base(id) + { + } + + public DateTime? CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs new file mode 100644 index 0000000000..def7e0d44c --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppFullAuditedEntityWithAudited.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Volo.Abp.Auditing.App.Entities +{ + [Audited] + public class AppFullAuditedEntityWithAudited : FullAuditedAggregateRoot + { + protected AppFullAuditedEntityWithAudited() + { + } + + public AppFullAuditedEntityWithAudited(Guid id, string name) + : base(id) + { + Name = name; + } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs index 0968bf2cc2..d6b2d2b167 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs @@ -17,6 +17,10 @@ namespace Volo.Abp.Auditing.App.EntityFrameworkCore public DbSet AppEntityWithPropertyHasAudited { get; set; } public DbSet AppEntityWithSelector { get; set; } + + public DbSet AppFullAuditedEntityWithAudited { get; set; } + + public DbSet AppEntityWithAuditedAndHasCustomAuditingProperties { get; set; } public AbpAuditingTestDbContext(DbContextOptions options) : base(options) diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index 2887eb8510..8c647c92c4 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -215,5 +216,63 @@ namespace Volo.Abp.Auditing #pragma warning restore 4014 } + private static List GetBaseAuditPropertyNames() + { + return new List + { + nameof(IHasCreationTime.CreationTime), + nameof(IMustHaveCreator.CreatorId), + nameof(IHasModificationTime.LastModificationTime), + nameof(IModificationAuditedObject.LastModifierId), + nameof(ISoftDelete.IsDeleted), + nameof(IHasDeletionTime.DeletionTime), + nameof(IDeletionAuditedObject.DeleterId) + }; + } + + [Fact] + public virtual async Task Should_Write_AuditLog_Ignoring_Base_Auditing_Properties_For_Entity_That_Has_Audited_Attribute() + { + using (var scope = _auditingManager.BeginScope()) + { + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(new AppFullAuditedEntityWithAudited(Guid.NewGuid(), "test name")); + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + _auditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 1 + && x.EntityChanges[0].PropertyChanges.Any(y => + !GetBaseAuditPropertyNames().Contains(y.PropertyName)))); +#pragma warning restore 4014 + } + + [Fact] + public virtual async Task Should_Write_AuditLog_Including_Custom_Base_Auditing_Properties_For_Entity_That_Has_Audited_Attribute() + { + using (var scope = _auditingManager.BeginScope()) + { + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(new AppEntityWithAuditedAndHasCustomAuditingProperties(Guid.NewGuid()) + { + CreationTime = DateTime.Now, + CreatorId = Guid.NewGuid(), + LastModificationTime = DateTime.Now, + LastModifierId = Guid.NewGuid(), + IsDeleted = true, + DeletionTime = DateTime.Now, + DeleterId = Guid.NewGuid() + }); + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + _auditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 1 + && x.EntityChanges[0].PropertyChanges + .Where(y => y.PropertyName != nameof(AppEntityWithAuditedAndHasCustomAuditingProperties + .ExtraProperties)) + .All(y => GetBaseAuditPropertyNames().Contains(y.PropertyName)))); +#pragma warning restore 4014 + } } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj index 3249de2c50..1d3e4b5dfd 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj b/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj index 6178700db1..1d3d553047 100644 --- a/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj +++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj b/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj index 7854d82b82..c8d0672d13 100644 --- a/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj +++ b/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj index c8763de75d..ca7f94918d 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj index ff97ea97d3..09a4861297 100644 --- a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..23df740630 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,57 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class DefaultAzureBlobNamingNormalizerProvider_Tests : AbpBlobStoringAzureTestCommonBase + { + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultAzureBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + [Fact] + public void NormalizeContainerName_Lowercase() + { + var filename = "ThisIsMyContainerName"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismycontainername"); + } + + [Fact] + public void NormalizeContainerName_Only_Letters_Numbers_Dash() + { + var filename = ",./this-i,./s-my-c,./ont,./ai+*/.=!@#$n^&*er-name.+/"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + [Fact] + public void NormalizeContainerName_Dash() + { + var filename = "-this--is----my-container----name-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + + [Fact] + public void NormalizeContainerName_Min_Length() + { + var filename = "a"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeGreaterThanOrEqualTo(3); + } + + + [Fact] + public void NormalizeContainerName_Max_Length() + { + var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeLessThanOrEqualTo(63); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj index b2741af89a..93165fa05a 100644 --- a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..b3f9fcf5d1 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,62 @@ +using System.Runtime; +using System.Runtime.InteropServices; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class DefaultFileSystemBlobNamingNormalizerProvider_Tests : AbpBlobStoringFileSystemTestBase + { + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultFileSystemBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + var _iosPlatformProvider = Substitute.For(); + _iosPlatformProvider.GetCurrentOSPlatform().Returns(OSPlatform.Windows); + services.AddSingleton(_iosPlatformProvider); + } + + [Fact] + public void NormalizeContainerName() + { + var filename = "thisismy:*?\"<>|foldername"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismyfoldername"); + } + + [Fact] + public void NormalizeBlobName() + { + var filename = "thisismy:*?\"<>|filename"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismyfilename"); + } + + [Theory] + [InlineData("/")] + [InlineData("\\")] + public void NormalizeContainerName_With_Directory(string delimiter) + { + var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}foldername"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe($"thisis{delimiter}my{delimiter}foldername"); + } + + [Theory] + [InlineData("/")] + [InlineData("\\")] + public void NormalizeBlobName_With_Directory(string delimiter) + { + var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}filename"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe($"thisis{delimiter}my{delimiter}filename"); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj index d9535ff176..130f95bb77 100644 --- a/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj new file mode 100644 index 0000000000..15f4bc8b9d --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo.Abp.Caching.StackExchangeRedis.Tests.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs new file mode 100644 index 0000000000..354ce3cc2b --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public abstract class AbpCachingStackExchangeRedisTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs new file mode 100644 index 0000000000..5d67ae743a --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisTestModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + [DependsOn( + typeof(AbpCachingStackExchangeRedisModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpCachingStackExchangeRedisTestModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs new file mode 100644 index 0000000000..660c8dbdb7 --- /dev/null +++ b/framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Caching.Distributed; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Caching.StackExchangeRedis +{ + public class AbpRedisCache_Tests : AbpCachingStackExchangeRedisTestBase + { + private readonly IDistributedCache _distributedCache; + + public AbpRedisCache_Tests() + { + _distributedCache = GetRequiredService(); + } + + [Fact] + public void Should_Replace_RedisCache() + { + (_distributedCache is AbpRedisCache).ShouldBeTrue(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj b/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj index 698278ee4c..1213685332 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj +++ b/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs index 14677a73bd..533362c851 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Testing; @@ -80,7 +81,6 @@ namespace Volo.Abp.Caching var personCache = GetRequiredService>(); var otherPersonCache = GetRequiredService>(); - var cacheKey = Guid.NewGuid().ToString(); const string personName = "john nash"; @@ -310,5 +310,34 @@ namespace Volo.Abp.Caching cacheItem2.ShouldBeNull(); } + [Fact] + public async Task Should_Set_And_Get_Multiple_Items_Async() + { + var personCache = GetRequiredService>(); + + await personCache.SetManyAsync(new[] + { + new KeyValuePair("john", new PersonCacheItem("John Nash")), + new KeyValuePair("thomas", new PersonCacheItem("Thomas Moore")) + }); + + var cacheItems = await personCache.GetManyAsync(new[] + { + "john", + "thomas", + "baris" //doesn't exist + }); + + cacheItems.Length.ShouldBe(3); + cacheItems[0].Key.ShouldBe("john"); + cacheItems[0].Value.Name.ShouldBe("John Nash"); + cacheItems[1].Key.ShouldBe("thomas"); + cacheItems[1].Value.Name.ShouldBe("Thomas Moore"); + cacheItems[2].Key.ShouldBe("baris"); + cacheItems[2].Value.ShouldBeNull(); + + (await personCache.GetAsync("john")).Name.ShouldBe("John Nash"); + (await personCache.GetAsync("baris")).ShouldBeNull(); + } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj b/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj index 23564716da..f16f39a6eb 100644 --- a/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj b/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj index 97aaa8a83f..d13f50b93b 100644 --- a/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj index bd646adf95..6be0cb02ab 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj b/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj index 1bb64ef52d..4402f1e9a8 100644 --- a/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj +++ b/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj b/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj index 35d7ad3950..c35edc1148 100644 --- a/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj +++ b/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj index cb917cea72..3422dcbcf6 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Entity_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Entity_Tests.cs new file mode 100644 index 0000000000..779ac3fe49 --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Entity_Tests.cs @@ -0,0 +1,139 @@ +using System; +using Shouldly; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.Domain.Entities +{ + public class Entity_Tests + { + [Fact] + public void EntityEquals_Should_Return_True_For_Same_Keys() + { + var idValue1 = Guid.NewGuid(); + var idValue2 = Guid.NewGuid(); + + new Person(idValue1).EntityEquals(new Person(idValue1)).ShouldBeTrue(); + + new Car(42).EntityEquals(new Car(42)).ShouldBeTrue(); + + new Product("a").EntityEquals(new Product("a")).ShouldBeTrue(); + + new Phone(idValue1, "123").EntityEquals(new Phone(idValue1, "123")).ShouldBeTrue(); + } + + [Fact] + public void EntityEquals_Should_Return_False_For_Different_Keys() + { + var idValue1 = Guid.NewGuid(); + var idValue2 = Guid.NewGuid(); + + new Person(idValue1).EntityEquals(new Person()).ShouldBeFalse(); + new Person(idValue1).EntityEquals(new Person(idValue2)).ShouldBeFalse(); + + new Car(42).EntityEquals(new Car()).ShouldBeFalse(); + new Car(42).EntityEquals(new Car(43)).ShouldBeFalse(); + + new Product("a").EntityEquals(new Product()).ShouldBeFalse(); + new Product("a").EntityEquals(new Product("b")).ShouldBeFalse(); + + new Phone(idValue1, "123").EntityEquals(new Phone()).ShouldBeFalse(); + new Phone(idValue1, "123").EntityEquals(new Phone(idValue1, null)).ShouldBeFalse(); + new Phone(idValue1, "123").EntityEquals(new Phone(idValue1, "321")).ShouldBeFalse(); + } + + [Fact] + public void EntityEquals_Should_Return_False_For_Both_Default_Keys() + { + new Person().EntityEquals(new Person()).ShouldBeFalse(); + + new Car().EntityEquals(new Car()).ShouldBeFalse(); + + new Product().EntityEquals(new Product()).ShouldBeFalse(); + + new Phone().EntityEquals(new Phone()).ShouldBeFalse(); + } + + [Fact] + public void Different_Tenants_With_Same_Keys_Considered_As_Different_Objects() + { + var tenantId1 = Guid.NewGuid(); + var tenantId2 = Guid.NewGuid(); + + new Car(42, tenantId1).EntityEquals(new Car(42)).ShouldBeFalse(); + new Car(42).EntityEquals(new Car(42, tenantId2)).ShouldBeFalse(); + new Car(42, tenantId1).EntityEquals(new Car(42, tenantId2)).ShouldBeFalse(); + } + + [Fact] + public void Same_Tenants_With_Same_Keys_Considered_As_Same_Objects() + { + var tenantId1 = Guid.NewGuid(); + + new Car(42).EntityEquals(new Car(42)).ShouldBeTrue(); + new Car(42, tenantId1).EntityEquals(new Car(42, tenantId1)).ShouldBeTrue(); + } + + public class Person : Entity + { + public Person() + { + } + + public Person(Guid id) + : base(id) + { + } + } + + public class Car : Entity, IMultiTenant + { + public Guid? TenantId { get; } + + public Car() + { + } + + public Car(int id, Guid? tenantId = null) + : base(id) + { + TenantId = tenantId; + } + } + + public class Product : Entity + { + public Product() + { + } + + public Product(string id) + : base(id) + { + } + } + + public class Phone : Entity + { + public Guid PersonId { get; set; } + + public string Number { get; set; } + + public Phone() + { + + } + + public Phone(Guid personId, string number) + { + PersonId = personId; + Number = number; + } + + public override object[] GetKeys() + { + return new Object[] {PersonId, Number}; + } + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj index 030eddb65a..23dd953416 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj index a154e6acaf..84fcc32477 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/EntityChangeEvents_Tests.cs index 2b94ee1ed9..9f4b67d089 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/EntityChangeEvents_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/EntityChangeEvents_Tests.cs @@ -4,6 +4,6 @@ namespace Volo.Abp.EntityFrameworkCore.DomainEvents { public class EntityChangeEvents_Tests : EntityChangeEvents_Tests { - + } } diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj b/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj index adeefbd45b..f45812c4a6 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj index bc375d9406..d5ea171e35 100644 --- a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj +++ b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj index 3c91d940e5..2ee95269ae 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo.Abp.Http.Client.IdentityModel.Web.Tests.csproj b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo.Abp.Http.Client.IdentityModel.Web.Tests.csproj index 704c3b57aa..78518bdda3 100644 --- a/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo.Abp.Http.Client.IdentityModel.Web.Tests.csproj +++ b/framework/test/Volo.Abp.Http.Client.IdentityModel.Web.Tests/Volo.Abp.Http.Client.IdentityModel.Web.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj b/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj index 193df5db4f..07708ce189 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj b/framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj index c0854a7633..3c1f9c96db 100644 --- a/framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj +++ b/framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj b/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj index 20c92c8cf8..56abd36ebb 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj +++ b/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs index 961f1ba0a4..71c2fafe2e 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs @@ -1,6 +1,5 @@ using System.Globalization; using System.Linq; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Shouldly; using Volo.Abp.Localization.TestResources.Base.CountryNames; diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/tr.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/tr.json index eddd61c662..ee1900e150 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/tr.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/tr.json @@ -5,7 +5,6 @@ "Car": "Araba", "CarPlural": "Araba", "MaxLenghtErrorMessage": "Bu alanın uzunluğu maksimum '{0}' karakter olabilir", - "Universe": "Evren", - "FortyTwo": "Kırk İki" + "Universe": "Evren" } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/tr.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/tr.json index df3fe71cd9..7023061d35 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/tr.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/tr.json @@ -1,6 +1,6 @@ { "culture": "tr", "texts": { - "SeeYou": "Görüşürüz" + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.MailKit.Tests/Volo.Abp.MailKit.Tests.csproj b/framework/test/Volo.Abp.MailKit.Tests/Volo.Abp.MailKit.Tests.csproj index bc5d991884..31c7001db5 100644 --- a/framework/test/Volo.Abp.MailKit.Tests/Volo.Abp.MailKit.Tests.csproj +++ b/framework/test/Volo.Abp.MailKit.Tests/Volo.Abp.MailKit.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj b/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj index 9b6adc9442..372ebd68f4 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs new file mode 100644 index 0000000000..3b0e1aa8f7 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.Auditing +{ + public class Auditing_Tests : Auditing_Tests + { + + } +} diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs new file mode 100644 index 0000000000..dea03664b0 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DataFilters +{ + public class SoftDelete_Tests : SoftDelete_Tests + { + + } +} diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs new file mode 100644 index 0000000000..52af309369 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DomainEvents +{ + public class DomainEvents_Tests : DomainEvents_Tests + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs new file mode 100644 index 0000000000..be5c014765 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DomainEvents +{ + public class EntityChangeEvents_Tests : EntityChangeEvents_Tests + { + + } +} diff --git a/framework/test/Volo.Abp.Minify.Tests/Volo.Abp.Minify.Tests.csproj b/framework/test/Volo.Abp.Minify.Tests/Volo.Abp.Minify.Tests.csproj index 69b159dd0f..2c14d9fba4 100644 --- a/framework/test/Volo.Abp.Minify.Tests/Volo.Abp.Minify.Tests.csproj +++ b/framework/test/Volo.Abp.Minify.Tests/Volo.Abp.Minify.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj index dc35828937..97758b7880 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj index 1c9ab1ab4b..3d95f00a8b 100644 --- a/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj +++ b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo.Abp.ObjectExtending.Tests.csproj b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo.Abp.ObjectExtending.Tests.csproj index 0a18e0c44b..212eeb7d5b 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo.Abp.ObjectExtending.Tests.csproj +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo.Abp.ObjectExtending.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.ObjectMapping.Tests/Volo.Abp.ObjectMapping.Tests.csproj b/framework/test/Volo.Abp.ObjectMapping.Tests/Volo.Abp.ObjectMapping.Tests.csproj index c4b54f9efd..23c59ef064 100644 --- a/framework/test/Volo.Abp.ObjectMapping.Tests/Volo.Abp.ObjectMapping.Tests.csproj +++ b/framework/test/Volo.Abp.ObjectMapping.Tests/Volo.Abp.ObjectMapping.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj index 6e99e2d1e8..41d152d1cb 100644 --- a/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj +++ b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj b/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj index 36df9d0a60..3efd1d9db7 100644 --- a/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj +++ b/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj index 70d52138a3..fadf2659d0 100644 --- a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj +++ b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj b/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj index 0ddc58ea96..b44ee82aae 100644 --- a/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj +++ b/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj b/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj index b733889bf0..0ff64ec1d8 100644 --- a/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj +++ b/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj index c2d9ab2007..35d5e3b71a 100644 --- a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj +++ b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs index fe4e0862f3..cb16a46439 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs @@ -84,5 +84,44 @@ namespace Volo.Abp.TestApp.Testing createdEventTriggered.ShouldBeTrue(); createdEtoTriggered.ShouldBeTrue(); } + + [Fact] + public async Task Multiple_Update_Should_Result_With_Single_Updated_Event_In_The_Same_Uow() + { + var personId = Guid.NewGuid(); + await PersonRepository.InsertAsync(new Person(personId, Guid.NewGuid().ToString("D"), 42)); + + var updateEventCount = 0; + var updatedAge = 0; + + DistributedEventBus.Subscribe>(eto => + { + updateEventCount++; + updatedAge = eto.Entity.Age; + return Task.CompletedTask; + }); + + using (var uow = GetRequiredService().Begin()) + { + var person = await PersonRepository.GetAsync(personId); + + person.Age = 43; + await PersonRepository.UpdateAsync(person, autoSave: true); + updateEventCount.ShouldBe(0); + + person.Age = 44; + await PersonRepository.UpdateAsync(person, autoSave: true); + updateEventCount.ShouldBe(0); + + person.Age = 45; + await PersonRepository.UpdateAsync(person, autoSave: true); + updateEventCount.ShouldBe(0); + + await uow.CompleteAsync(); + } + + updateEventCount.ShouldBe(1); + updatedAge.ShouldBe(45); + } } } diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj index 0d230552e8..0895567843 100644 --- a/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj index bf48039303..2ba5372364 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj b/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj index 32bd3e4af5..c6b401eec1 100644 --- a/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj +++ b/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj b/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj index 7bf3df7ca5..13f57717d5 100644 --- a/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj +++ b/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj index 7dcfdc83c5..bcb9dcbf74 100644 --- a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj +++ b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj @@ -4,6 +4,7 @@ netcoreapp3.1 + true @@ -14,7 +15,8 @@ - + + diff --git a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/MyResources/js/my{test}.2.9.min.js b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/MyResources/js/my{test}.2.9.min.js new file mode 100644 index 0000000000..1c74f24545 --- /dev/null +++ b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/MyResources/js/my{test}.2.9.min.js @@ -0,0 +1 @@ +//my{test}.2.9.min.js-content \ No newline at end of file diff --git a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/VirtualFileProvider_Tests.cs b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/VirtualFileProvider_Tests.cs index 2243d9a11a..aae01ff912 100644 --- a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/VirtualFileProvider_Tests.cs +++ b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/VirtualFileProvider_Tests.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.DependencyInjection; @@ -34,6 +33,22 @@ namespace Volo.Abp.VirtualFileSystem Encoding.UTF8.GetString(stream.GetAllBytes()).ShouldBe("//jquery-3-1-1-min.js-contents"); } } + + [Fact] + public void Should_Define_And_Get_Embedded_Resources_With_Special_Chars() + { + //Act + var resource = _virtualFileProvider.GetFileInfo("/js/my{test}.2.9.min.js"); + + //Assert + resource.ShouldNotBeNull(); + resource.Exists.ShouldBeTrue(); + + using (var stream = resource.CreateReadStream()) + { + Encoding.UTF8.GetString(stream.GetAllBytes()).ShouldBe("//my{test}.2.9.min.js-content"); + } + } [Fact] public void Should_Define_And_Get_Embedded_Directory_Contents() @@ -45,7 +60,9 @@ namespace Volo.Abp.VirtualFileSystem contents.Exists.ShouldNotBeNull(); var contentList = contents.ToList(); + contentList.ShouldContain(x => x.Name == "jquery-3-1-1-min.js"); + contentList.ShouldContain(x => x.Name == "my{test}.2.9.min.js"); } [Theory] @@ -70,7 +87,9 @@ namespace Volo.Abp.VirtualFileSystem { Configure(options => { - options.FileSets.AddEmbedded("Volo.Abp.VirtualFileSystem.MyResources"); + options.FileSets.AddEmbedded( + baseFolder: "/Volo/Abp/VirtualFileSystem/MyResources" + ); }); } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json index 7cf2cf7be6..f0dbdaadb6 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json @@ -40,6 +40,9 @@ "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Is self-registration enabled", "Description:Abp.Account.IsSelfRegistrationEnabled": "Whether a user can register the account by him or herself.", "DisplayName:Abp.Account.EnableLocalLogin": "Authenticate with a local account", - "Description:Abp.Account.EnableLocalLogin": "Indicates if the server will allow users to authenticate with a local account." + "Description:Abp.Account.EnableLocalLogin": "Indicates if the server will allow users to authenticate with a local account.", + "LoggedOutTitle": "Signed Out", + "LoggedOutText": "You have been signed out and you will be redirected soon.", + "ReturnToText": "Click here to redirect to {0}" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json index 60d5f40634..3938a34984 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json @@ -40,6 +40,9 @@ "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "self-registration etkin mi ?", "Description:Abp.Account.IsSelfRegistrationEnabled": "Bir kullanıcının hesabı kendisi tarafından kaydedip kaydedememesidir.", "DisplayName:Abp.Account.EnableLocalLogin": "Yerel bir hesapla kimlik doğrulaması", - "Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir." + "Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir.", + "LoggedOutTitle": "Çıkış Yaptınız", + "LoggedOutText": "Çıkış yaptınız ve birazdan yönlendirileceksiniz.", + "ReturnToText": "{0} uygulamasına dönmek için tıklayın." } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs index 9a9f3caa4e..b69383d26f 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/RegisterDto.cs @@ -1,22 +1,23 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; using Volo.Abp.Identity; +using Volo.Abp.Validation; namespace Volo.Abp.Account { public class RegisterDto { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string EmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj index 1ddc2323d1..a11fb89e1a 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj +++ b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj @@ -11,7 +11,7 @@ - + diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs index ab403ea5a1..43857cc111 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs @@ -1,6 +1,8 @@ -using IdentityServer4.Services; +using System.Security.Claims; +using IdentityServer4.Services; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Account.Web.Pages.Account @@ -24,13 +26,20 @@ namespace Volo.Abp.Account.Web.Pages.Account if (!string.IsNullOrEmpty(logoutId)) { var logoutContext = await Interaction.GetLogoutContextAsync(logoutId); + await SignInManager.SignOutAsync(); - var postLogoutUri = logoutContext.PostLogoutRedirectUri; + HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - if (!string.IsNullOrEmpty(postLogoutUri)) + LoggedOutModel vm = new LoggedOutModel() { - return Redirect(postLogoutUri); - } + PostLogoutRedirectUri = logoutContext?.PostLogoutRedirectUri, + ClientName = logoutContext?.ClientName, + SignOutIframeUrl = logoutContext?.SignOutIFrameUrl + }; + + Logger.LogInformation($"Redirecting to LoggedOut Page..."); + + return RedirectToPage("./LoggedOut", vm); } if (ReturnUrl != null) @@ -38,6 +47,8 @@ namespace Volo.Abp.Account.Web.Pages.Account return LocalRedirect(ReturnUrl); } + Logger.LogInformation( + $"IdentityServerSupportedLogoutModel couldn't find postLogoutUri... Redirecting to:/Account/Login.."); return RedirectToPage("/Account/Login"); } } diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json deleted file mode 100644 index aca3a5b165..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:49583/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Account.Web.IdentityServer": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:49584/" - } - } -} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj index 17a164e612..ca27f60695 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj @@ -21,8 +21,6 @@ - - diff --git a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/Models/UserLoginInfo.cs b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/Models/UserLoginInfo.cs index fe86445a85..ef3f399477 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/Models/UserLoginInfo.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/Models/UserLoginInfo.cs @@ -17,7 +17,5 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers.Models public string Password { get; set; } public bool RememberMe { get; set; } - - public Guid? TenanId { get; set; } } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml new file mode 100644 index 0000000000..0e7b14236a --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml @@ -0,0 +1,31 @@ +@page "/Account/LoggedOut" +@model Volo.Abp.Account.Web.Pages.Account.LoggedOutModel +@using Volo.Abp.Account.Localization +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Web.Pages.Account +@inject IHtmlLocalizer L + +@section scripts { + + + +} + +@section styles { + +} + + + + @L["LoggedOutTitle"] + @L["LoggedOutText"] + @if (Model.PostLogoutRedirectUri != null) + { + @L["ReturnToText", Model.ClientName] + } + @if (Model.SignOutIframeUrl != null) + { + + } + + diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs new file mode 100644 index 0000000000..6023732975 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class LoggedOutModel : AccountPageModel + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ClientName { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string SignOutIframeUrl { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string PostLogoutRedirectUri { get; set; } + + public virtual Task OnGetAsync() + { + return Task.FromResult(Page()); + } + + public virtual Task OnPostAsync() + { + return Task.FromResult(Page()); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css new file mode 100644 index 0000000000..75dcda385e --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.css @@ -0,0 +1,5 @@ +.logoutiframe { + display: none; + width:0; + height: 0; +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js new file mode 100644 index 0000000000..950bf65719 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.js @@ -0,0 +1,5 @@ +document.addEventListener("DOMContentLoaded", function (event) { + setTimeout(function () { + window.location = document.getElementById("redirectButton").getAttribute("href"); + }, 3000) +}); diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 34a084bd3d..5251cb12f1 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -254,11 +254,11 @@ namespace Volo.Abp.Account.Web.Pages.Account public class LoginInputModel { [Required] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string UserNameOrEmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs index deed56b736..3c779dc870 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Volo.Abp.Identity; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.Account.Web.Pages.Account { @@ -36,44 +37,45 @@ namespace Volo.Abp.Account.Web.Pages.Account public class ChangePasswordInfoModel { [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:CurrentPassword")] [DataType(DataType.Password)] public string CurrentPassword { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:NewPassword")] [DataType(DataType.Password)] public string NewPassword { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [Display(Name = "DisplayName:NewPasswordConfirm")] [DataType(DataType.Password)] public string NewPasswordConfirm { get; set; } } + public class PersonalSettingsInfoModel { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] [Display(Name = "DisplayName:UserName")] public string UserName { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] [Display(Name = "DisplayName:Email")] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] [Display(Name = "DisplayName:Name")] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] [Display(Name = "DisplayName:Surname")] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] [Display(Name = "DisplayName:PhoneNumber")] public string PhoneNumber { get; set; } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 87c7131305..f6d626dd63 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -9,6 +9,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Identity; using Volo.Abp.Settings; using Volo.Abp.Uow; +using Volo.Abp.Validation; using IdentityUser = Volo.Abp.Identity.IdentityUser; namespace Volo.Abp.Account.Web.Pages.Account @@ -74,16 +75,16 @@ namespace Volo.Abp.Account.Web.Pages.Account public class PostInput { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string EmailAddress { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json b/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json deleted file mode 100644 index 8dab775bff..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56009/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Account.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56013/" - } - } -} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj index a77cbad333..42918e34d4 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj @@ -25,8 +25,6 @@ - - diff --git a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj index f4c7094d4f..779c2d2d25 100644 --- a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj +++ b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj @@ -6,13 +6,13 @@ - - - + + + - + diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogActionConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogActionConsts.cs index 86652aacec..5f1967804e 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogActionConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogActionConsts.cs @@ -2,10 +2,19 @@ { public class AuditLogActionConsts { - public const int MaxServiceNameLength = 256; + /// + /// Default value: 256 + /// + public static int MaxServiceNameLength { get; set; } = 256; - public const int MaxMethodNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxMethodNameLength { get; set; } = 128; - public const int MaxParametersLength = 2000; + /// + /// Default value: 2000 + /// + public static int MaxParametersLength { get; set; } = 2000; } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs index 504e7451e0..4a8c723b76 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs @@ -2,31 +2,67 @@ { public static class AuditLogConsts { - public const int MaxApplicationNameLength = 96; + /// + /// Default value: 96 + /// + public static int MaxApplicationNameLength { get; set; } = 96; - public const int MaxClientIpAddressLength = 64; + /// + /// Default value: 64 + /// + public static int MaxClientIpAddressLength { get; set; } = 64; - public const int MaxClientNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxClientNameLength { get; set; } = 128; - public const int MaxClientIdLength = 64; + /// + /// Default value: 64 + /// + public static int MaxClientIdLength { get; set; } = 64; - public const int MaxCorrelationIdLength = 64; + /// + /// Default value: 64 + /// + public static int MaxCorrelationIdLength { get; set; } = 64; - public const int MaxBrowserInfoLength = 512; + /// + /// Default value: 512 + /// + public static int MaxBrowserInfoLength { get; set; } = 512; - public const int MaxExceptionsLength = 4000; + /// + /// Default value: 4000 + /// + public static int MaxExceptionsLength { get; set; } = 4000; //TODO: Replace with MaxExceptionsLength in v3.0 public static int MaxExceptionsLengthValue { get; set; } = MaxExceptionsLength; - public const int MaxCommentsLength = 256; + /// + /// Default value: 256 + /// + public static int MaxCommentsLength { get; set; } = 256; - public const int MaxUrlLength = 256; + /// + /// Default value: 256 + /// + public static int MaxUrlLength { get; set; } = 256; - public const int MaxHttpMethodLength = 16; + /// + /// Default value: 16 + /// + public static int MaxHttpMethodLength { get; set; } = 16; - public const int MaxUserNameLength = 256; + /// + /// Default value: 256 + /// + public static int MaxUserNameLength { get; set; } = 256; - public const int MaxTenantNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxTenantNameLength { get; set; } = 64; } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityChangeConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityChangeConsts.cs index d19407cfa4..b7f88db3e2 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityChangeConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityChangeConsts.cs @@ -2,8 +2,14 @@ { public class EntityChangeConsts { - public const int MaxEntityTypeFullNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxEntityTypeFullNameLength { get; set; } = 128; - public const int MaxEntityIdLength = 128; + /// + /// Default value: 128 + /// + public static int MaxEntityIdLength { get; set; } = 128; } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityPropertyChangeConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityPropertyChangeConsts.cs index 2ac9204395..8d592e06aa 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityPropertyChangeConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityPropertyChangeConsts.cs @@ -2,12 +2,24 @@ { public class EntityPropertyChangeConsts { - public const int MaxNewValueLength = 512; + /// + /// Default value: 512 + /// + public static int MaxNewValueLength { get; set; } = 512; - public const int MaxOriginalValueLength = 512; + /// + /// Default value: 512 + /// + public static int MaxOriginalValueLength { get; set; } = 512; - public const int MaxPropertyNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxPropertyNameLength { get; set; } = 128; - public const int MaxPropertyTypeFullNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxPropertyTypeFullNameLength { get; set; } = 64; } } diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj index c85e570040..6287d732e2 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj index ffc1906dce..34130bfca5 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo.Abp.AuditLogging.MongoDB.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo.Abp.AuditLogging.TestBase.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo.Abp.AuditLogging.TestBase.csproj index ced62e3c1b..e62925a488 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo.Abp.AuditLogging.TestBase.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo.Abp.AuditLogging.TestBase.csproj @@ -14,12 +14,12 @@ - - + + - + diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo.Abp.AuditLogging.Tests.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo.Abp.AuditLogging.Tests.csproj index ccfb554d58..ab703cdca3 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo.Abp.AuditLogging.Tests.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.Tests/Volo.Abp.AuditLogging.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Volo.Abp.BackgroundJobs.DemoApp.Shared.csproj b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Volo.Abp.BackgroundJobs.DemoApp.Shared.csproj index 3c942ccf89..cf5ed0ce08 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Volo.Abp.BackgroundJobs.DemoApp.Shared.csproj +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Volo.Abp.BackgroundJobs.DemoApp.Shared.csproj @@ -7,7 +7,7 @@ - + diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.Designer.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.Designer.cs deleted file mode 100644 index acab1e91e7..0000000000 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.Designer.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Volo.Abp.BackgroundJobs; -using Volo.Abp.BackgroundJobs.DemoApp.Db; - -namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations -{ - [DbContext(typeof(DemoAppDbContext))] - [Migration("20180724105416_BackgroundJobs_Initial")] - partial class BackgroundJobs_Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("IsAbandoned") - .ValueGeneratedOnAdd() - .HasDefaultValue(false); - - b.Property("JobArgs") - .IsRequired() - .HasMaxLength(1048576); - - b.Property("JobName") - .IsRequired() - .HasMaxLength(128); - - b.Property("LastTryTime"); - - b.Property("NextTryTime"); - - b.Property("Priority") - .ValueGeneratedOnAdd() - .HasDefaultValue((byte)15); - - b.Property("TryCount") - .ValueGeneratedOnAdd() - .HasDefaultValue((short)0); - - b.HasKey("Id"); - - b.HasIndex("IsAbandoned", "NextTryTime"); - - b.ToTable("AbpBackgroundJobs"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.cs deleted file mode 100644 index 25aa036ed3..0000000000 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations -{ - public partial class AggragateRoot_Changes : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "ConcurrencyStamp", - table: "AbpBackgroundJobs", - nullable: true); - - migrationBuilder.AddColumn( - name: "ExtraProperties", - table: "AbpBackgroundJobs", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "ConcurrencyStamp", - table: "AbpBackgroundJobs"); - - migrationBuilder.DropColumn( - name: "ExtraProperties", - table: "AbpBackgroundJobs"); - } - } -} diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.Designer.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.Designer.cs similarity index 59% rename from modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.Designer.cs rename to modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.Designer.cs index d4ed20351c..40c0adcee7 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20181211140407_AggragateRoot_Changes.Designer.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.Designer.cs @@ -5,58 +5,73 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Volo.Abp.BackgroundJobs; using Volo.Abp.BackgroundJobs.DemoApp.Db; +using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations { [DbContext(typeof(DemoAppDbContext))] - [Migration("20181211140407_AggragateRoot_Changes")] - partial class AggragateRoot_Changes + [Migration("20200624023633_BackgroundJobs_Initial")] + partial class BackgroundJobs_Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); - b.Property("ConcurrencyStamp"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") - .HasColumnName("CreationTime"); + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); b.Property("IsAbandoned") .ValueGeneratedOnAdd() + .HasColumnType("bit") .HasDefaultValue(false); b.Property("JobArgs") .IsRequired() + .HasColumnType("nvarchar(max)") .HasMaxLength(1048576); b.Property("JobName") .IsRequired() + .HasColumnType("nvarchar(128)") .HasMaxLength(128); - b.Property("LastTryTime"); + b.Property("LastTryTime") + .HasColumnType("datetime2"); - b.Property("NextTryTime"); + b.Property("NextTryTime") + .HasColumnType("datetime2"); b.Property("Priority") .ValueGeneratedOnAdd() + .HasColumnType("tinyint") .HasDefaultValue((byte)15); b.Property("TryCount") .ValueGeneratedOnAdd() + .HasColumnType("smallint") .HasDefaultValue((short)0); b.HasKey("Id"); diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.cs similarity index 91% rename from modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.cs rename to modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.cs index 76202a0748..eebffa859a 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20180724105416_BackgroundJobs_Initial.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.cs @@ -12,6 +12,8 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations columns: table => new { Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), JobName = table.Column(maxLength: 128, nullable: false), JobArgs = table.Column(maxLength: 1048576, nullable: false), TryCount = table.Column(nullable: false, defaultValue: (short)0), diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs index d58d7a4966..fa77fd75ca 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs @@ -4,8 +4,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Volo.Abp.BackgroundJobs; using Volo.Abp.BackgroundJobs.DemoApp.Db; +using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations { @@ -16,45 +16,60 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); - b.Property("ConcurrencyStamp"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") - .HasColumnName("CreationTime"); + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); b.Property("IsAbandoned") .ValueGeneratedOnAdd() + .HasColumnType("bit") .HasDefaultValue(false); b.Property("JobArgs") .IsRequired() + .HasColumnType("nvarchar(max)") .HasMaxLength(1048576); b.Property("JobName") .IsRequired() + .HasColumnType("nvarchar(128)") .HasMaxLength(128); - b.Property("LastTryTime"); + b.Property("LastTryTime") + .HasColumnType("datetime2"); - b.Property("NextTryTime"); + b.Property("NextTryTime") + .HasColumnType("datetime2"); b.Property("Priority") .ValueGeneratedOnAdd() + .HasColumnType("tinyint") .HasDefaultValue((byte)15); b.Property("TryCount") .ValueGeneratedOnAdd() + .HasColumnType("smallint") .HasDefaultValue((short)0); b.HasKey("Id"); diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj index e6f1a834ec..0d8388c610 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj @@ -6,7 +6,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo/Abp/BackgroundJobs/BackgroundJobRecordConsts.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo/Abp/BackgroundJobs/BackgroundJobRecordConsts.cs index f4418bc423..01f935b30e 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo/Abp/BackgroundJobs/BackgroundJobRecordConsts.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo/Abp/BackgroundJobs/BackgroundJobRecordConsts.cs @@ -2,8 +2,14 @@ { public static class BackgroundJobRecordConsts { - public const int MaxJobNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxJobNameLength { get; set; } = 128; - public const int MaxJobArgsLength = 1024 * 1024; + /// + /// Default value: 1024 * 1024 + /// + public static int MaxJobArgsLength { get; set; } = 1024 * 1024; } } diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj index 1326240199..4c835cbd51 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj index e419b3302a..3246aefa4f 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj index 3878875e65..496f95b373 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj index 8563f2b04a..46d6ea7b8e 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj @@ -14,12 +14,12 @@ - - + + - + diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj index 61af5fc4ce..43455e655a 100644 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj @@ -20,13 +20,13 @@ - + - - + + diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs deleted file mode 100644 index 23fe326a47..0000000000 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -using System; -using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Volo.Abp.EntityFrameworkCore; - -namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations -{ - [DbContext(typeof(BlobStoringHostDbContext))] - [Migration("20200531134550_Initial")] - partial class Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "3.1.4") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("ContainerId") - .HasColumnType("uniqueidentifier"); - - b.Property("Content") - .HasColumnType("varbinary(max)") - .HasMaxLength(2147483647); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("TenantId") - .HasColumnName("TenantId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("TenantId", "ContainerId", "Name"); - - b.ToTable("AbpBlobs"); - }); - - modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); - - b.Property("TenantId") - .HasColumnName("TenantId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("TenantId", "Name"); - - b.ToTable("AbpBlobContainers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs deleted file mode 100644 index 4411063f94..0000000000 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations -{ - public partial class FK_Added : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_AbpBlobs_ContainerId", - table: "AbpBlobs", - column: "ContainerId"); - - migrationBuilder.AddForeignKey( - name: "FK_AbpBlobs_AbpBlobContainers_ContainerId", - table: "AbpBlobs", - column: "ContainerId", - principalTable: "AbpBlobContainers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_AbpBlobs_AbpBlobContainers_ContainerId", - table: "AbpBlobs"); - - migrationBuilder.DropIndex( - name: "IX_AbpBlobs_ContainerId", - table: "AbpBlobs"); - } - } -} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.Designer.cs similarity index 92% rename from modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs rename to modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.Designer.cs index 1214d7f500..5aa34b1eda 100644 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.Designer.cs @@ -11,15 +11,15 @@ using Volo.Abp.EntityFrameworkCore; namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations { [DbContext(typeof(BlobStoringHostDbContext))] - [Migration("20200531143839_FK_Added")] - partial class FK_Added + [Migration("20200624023744_Initial")] + partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -32,7 +32,8 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ContainerId") .HasColumnType("uniqueidentifier"); @@ -72,7 +73,8 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ExtraProperties") .HasColumnName("ExtraProperties") diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.cs similarity index 76% rename from modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs rename to modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.cs index d1ee75ac7f..bc8d412cb5 100644 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.cs @@ -13,7 +13,7 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), TenantId = table.Column(nullable: true), Name = table.Column(maxLength: 128, nullable: false) }, @@ -28,7 +28,7 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), ContainerId = table.Column(nullable: false), TenantId = table.Column(nullable: true), Name = table.Column(maxLength: 256, nullable: false), @@ -37,6 +37,12 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations constraints: table => { table.PrimaryKey("PK_AbpBlobs", x => x.Id); + table.ForeignKey( + name: "FK_AbpBlobs_AbpBlobContainers_ContainerId", + column: x => x.ContainerId, + principalTable: "AbpBlobContainers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( @@ -44,6 +50,11 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations table: "AbpBlobContainers", columns: new[] { "TenantId", "Name" }); + migrationBuilder.CreateIndex( + name: "IX_AbpBlobs_ContainerId", + table: "AbpBlobs", + column: "ContainerId"); + migrationBuilder.CreateIndex( name: "IX_AbpBlobs_TenantId_ContainerId_Name", table: "AbpBlobs", @@ -53,10 +64,10 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "AbpBlobContainers"); + name: "AbpBlobs"); migrationBuilder.DropTable( - name: "AbpBlobs"); + name: "AbpBlobContainers"); } } } diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs index 278eb1f72c..796c68b0ad 100644 --- a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -30,7 +30,8 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ContainerId") .HasColumnType("uniqueidentifier"); @@ -70,7 +71,8 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ExtraProperties") .HasColumnName("ExtraProperties") diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs index 0a71451f9f..22d127a1f1 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs @@ -2,6 +2,9 @@ { public static class DatabaseContainerConsts { - public const int MaxNameLength = 128; + /// + /// Default value: 128. + /// + public static int MaxNameLength { get; set; } = 128; } } \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj index 20de7c154e..a6ff223c3a 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj index 8bdc5080aa..04bdd5071a 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj @@ -6,10 +6,10 @@ - - - - + + + + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj index 9b37676068..48196ab38f 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj index 8dfa603e98..196cdce7bb 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj @@ -6,12 +6,12 @@ - - + + - + diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj index 6e269e702a..c264c00f41 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Create.cshtml.cs b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Create.cshtml.cs index b2d89fbd55..6f4bae5e63 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Create.cshtml.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Create.cshtml.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; using Volo.Blogging.Admin.Blogs; using Volo.Blogging.Blogs; @@ -44,14 +45,14 @@ namespace Volo.Blogging.Admin.Pages.Blogging.Admin.Blogs public class BlogCreateModalView { [Required] - [StringLength(BlogConsts.MaxNameLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] public string Name { get; set; } [Required] - [StringLength(BlogConsts.MaxShortNameLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxShortNameLength))] public string ShortName { get; set; } - [StringLength(BlogConsts.MaxDescriptionLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxDescriptionLength))] public string Description { get; set; } } diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Edit.cshtml.cs b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Edit.cshtml.cs index 536810e015..8d0b2ba978 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Edit.cshtml.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Edit.cshtml.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; using Volo.Blogging.Admin.Blogs; using Volo.Blogging.Blogs; @@ -58,14 +59,14 @@ namespace Volo.Blogging.Admin.Pages.Blogging.Admin.Blogs public Guid Id { get; set; } [Required] - [StringLength(BlogConsts.MaxNameLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] public string Name { get; set; } [Required] - [StringLength(BlogConsts.MaxShortNameLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxShortNameLength))] public string ShortName { get; set; } - [StringLength(BlogConsts.MaxDescriptionLength)] + [DynamicStringLength(typeof(BlogConsts), nameof(BlogConsts.MaxDescriptionLength))] public string Description { get; set; } } } diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json b/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json deleted file mode 100644 index 64a5fd912c..0000000000 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Blogging.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50014/" - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj index d15cf9c770..fdbe0dd150 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj @@ -19,7 +19,7 @@ - + @@ -28,7 +28,6 @@ - diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/BloggingWebConsts.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/BloggingWebConsts.cs index ae4eb32dff..d9eb284ac6 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/BloggingWebConsts.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/BloggingWebConsts.cs @@ -6,7 +6,10 @@ namespace Volo.Blogging { public class FileUploading { - public const int MaxFileSize = 5242880; //5MB + /// + /// Default value: 5242880 + /// + public static int MaxFileSize { get; set; } = 5242880; //5MB public static int MaxFileSizeAsMegabytes => Convert.ToInt32((MaxFileSize / 1024f) / 1024f); } diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs index 164e4f691f..96cfbd6859 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; namespace Volo.Blogging.Posts { @@ -8,22 +9,22 @@ namespace Volo.Blogging.Posts public Guid BlogId { get; set; } [Required] - [StringLength(PostConsts.MaxTitleLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxTitleLength))] public string Title { get; set; } [Required] public string CoverImage { get; set; } [Required] - [StringLength(PostConsts.MaxUrlLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxUrlLength))] public string Url { get; set; } - [StringLength(PostConsts.MaxContentLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxContentLength))] public string Content { get; set; } public string Tags { get; set; } - [StringLength(PostConsts.MaxDescriptionLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxDescriptionLength))] public string Description { get; set; } } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs index 502a4364e7..b4b428207c 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs @@ -2,10 +2,19 @@ { public static class BlogConsts { - public const int MaxNameLength = 256; + /// + /// Default value: 256 + /// + public static int MaxNameLength { get; set; } = 256; - public const int MaxShortNameLength = 32; + /// + /// Default value: 32 + /// + public static int MaxShortNameLength { get; set; } = 32; - public const int MaxDescriptionLength = 1024; + /// + /// Default value: 1024 + /// + public static int MaxDescriptionLength { get; set; } = 1024; } } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs index 34cc655fb3..359b322cbf 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs @@ -2,6 +2,9 @@ { public class CommentConsts { - public const int MaxTextLength = 1024; + /// + /// Default value: 1024 + /// + public static int MaxTextLength { get; set; } = 1024; } } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs index 665984dfbb..de91caa2d3 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs @@ -2,16 +2,34 @@ { public static class PostConsts { - public const int MaxTitleLength = 512; + /// + /// Default value: 512 + /// + public static int MaxTitleLength { get; set; } = 512; - public const int MaxUrlLength = 64; + /// + /// Default value: 64 + /// + public static int MaxUrlLength { get; set; } = 64; - public const int MaxContentLength = 1024 * 1024; //1MB + /// + /// Default value: 1024 * 1024 + /// + public static int MaxContentLength { get; set; } = 1024 * 1024; //1MB - public const int MaxDescriptionLength = 1000; + /// + /// Default value: 1000 + /// + public static int MaxDescriptionLength { get; set; } = 1000; - public const int MaxTitleLengthToBeSeoFriendly = 60; + /// + /// Default value: 60 + /// + public static int MaxTitleLengthToBeSeoFriendly { get; set; } = 60; - public const int MaxSeoFriendlyDescriptionLength = 200; + /// + /// Default value: 200 + /// + public static int MaxSeoFriendlyDescriptionLength { get; set; } = 200; } } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs index c75ccd4184..ad2be87ca8 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs @@ -2,8 +2,14 @@ { public class TagConsts { - public const int MaxNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxNameLength { get; set; } = 64; - public const int MaxDescriptionLength = 512; + /// + /// Default value: 512 + /// + public static int MaxDescriptionLength { get; set; } = 512; } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs index c2ebcc441b..6d998a7172 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Validation; using Volo.Blogging.Blogs; using Volo.Blogging.Pages.Blogs.Shared.Helpers; using Volo.Blogging.Posts; @@ -85,7 +86,7 @@ namespace Volo.Blogging.Pages.Blog.Posts public Guid BlogId { get; set; } [Required] - [StringLength(PostConsts.MaxTitleLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxTitleLength))] public string Title { get; set; } [Required] @@ -93,14 +94,14 @@ namespace Volo.Blogging.Pages.Blog.Posts public string CoverImage { get; set; } [Required] - [StringLength(PostConsts.MaxUrlLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxUrlLength))] public string Url { get; set; } [HiddenInput] - [StringLength(PostConsts.MaxContentLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxContentLength))] public string Content { get; set; } - [StringLength(PostConsts.MaxDescriptionLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxDescriptionLength))] public string Description { get; set; } public string Tags { get; set; } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml.cs b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml.cs index 41c8f43ddd..3930a79900 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Validation; using Volo.Blogging.Blogs; using Volo.Blogging.Blogs.Dtos; using Volo.Blogging.Pages.Blogs.Shared.Helpers; @@ -79,7 +80,7 @@ namespace Volo.Blogging.Pages.Blog.Posts public Guid BlogId { get; set; } [Required] - [StringLength(PostConsts.MaxTitleLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxTitleLength))] public string Title { get; set; } [Required] @@ -87,16 +88,16 @@ namespace Volo.Blogging.Pages.Blog.Posts public string CoverImage { get; set; } [Required] - [StringLength(PostConsts.MaxUrlLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxUrlLength))] public string Url { get; set; } [HiddenInput] - [StringLength(PostConsts.MaxContentLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxContentLength))] public string Content { get; set; } public string Tags { get; set; } - [StringLength(PostConsts.MaxDescriptionLength)] + [DynamicStringLength(typeof(PostConsts), nameof(PostConsts.MaxDescriptionLength))] public string Description { get; set; } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json b/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json deleted file mode 100644 index 64a5fd912c..0000000000 --- a/modules/blogging/src/Volo.Blogging.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50000/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Blogging.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50014/" - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj index 3760fa0539..203c142de9 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj @@ -19,7 +19,7 @@ - + @@ -28,9 +28,7 @@ - - diff --git a/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj b/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj index 2bc4009d26..6776e49a9c 100644 --- a/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.Application.Tests/Volo.Blogging.Application.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj index 4c77689067..776aaf3657 100644 --- a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo.Blogging.Domain.Tests.csproj @@ -2,11 +2,11 @@ netcoreapp3.1 - + - + diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj index 1061e14347..01d894de88 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj index d58c4ce66f..8eaa145ee7 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo.Blogging.MongoDB.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/blogging/test/Volo.Blogging.TestBase/Volo.Blogging.TestBase.csproj b/modules/blogging/test/Volo.Blogging.TestBase/Volo.Blogging.TestBase.csproj index 39cc02fafa..eae856530d 100644 --- a/modules/blogging/test/Volo.Blogging.TestBase/Volo.Blogging.TestBase.csproj +++ b/modules/blogging/test/Volo.Blogging.TestBase/Volo.Blogging.TestBase.csproj @@ -12,12 +12,12 @@ - - + + - + diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json b/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json deleted file mode 100644 index 2f043da627..0000000000 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51023/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.ClientSimulation.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:51024/" - } - } -} \ No newline at end of file diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj index e8358805b7..aa3ecf2dac 100644 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj +++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -27,9 +27,7 @@ - - diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.Designer.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.Designer.cs similarity index 98% rename from modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.Designer.cs rename to modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.Designer.cs index 5fbced9e4a..2fb2a6782a 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.Designer.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.Designer.cs @@ -11,7 +11,7 @@ using VoloDocs.EntityFrameworkCore; namespace VoloDocs.EntityFrameworkCore.Migrations { [DbContext(typeof(VoloDocsDbContext))] - [Migration("20200617102723_Initial")] + [Migration("20200624023838_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -31,10 +31,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .IsRequired() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("Description") .HasColumnType("nvarchar(256)") @@ -79,10 +78,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .IsRequired() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ExtraProperties") .HasColumnName("ExtraProperties") @@ -164,7 +162,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") .HasColumnName("CreationTime") @@ -437,7 +436,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") .HasColumnName("CreationTime") @@ -594,7 +594,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("Content") .IsRequired() @@ -692,7 +693,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("DefaultDocumentName") .IsRequired() diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.cs similarity index 98% rename from modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.cs rename to modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.cs index 179387743c..66b5265a33 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200617102723_Initial.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200624023838_Initial.cs @@ -13,7 +13,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), Name = table.Column(maxLength: 256, nullable: false), Required = table.Column(nullable: false), IsStatic = table.Column(nullable: false), @@ -33,7 +33,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), CreationTime = table.Column(nullable: false), CreatorId = table.Column(nullable: true), LastModificationTime = table.Column(nullable: true), @@ -78,7 +78,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), TenantId = table.Column(nullable: true), Name = table.Column(maxLength: 256, nullable: false), NormalizedName = table.Column(maxLength: 256, nullable: false), @@ -112,7 +112,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), CreationTime = table.Column(nullable: false), CreatorId = table.Column(nullable: true), LastModificationTime = table.Column(nullable: true), @@ -148,7 +148,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), ProjectId = table.Column(nullable: false), Name = table.Column(maxLength: 255, nullable: false), Version = table.Column(maxLength: 128, nullable: false), @@ -176,7 +176,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { Id = table.Column(nullable: false), ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 40, nullable: true), Name = table.Column(maxLength: 128, nullable: false), ShortName = table.Column(maxLength: 32, nullable: false), Format = table.Column(nullable: true), diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs index 43feaed625..73b261e454 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs @@ -29,10 +29,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .IsRequired() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("Description") .HasColumnType("nvarchar(256)") @@ -77,10 +76,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .IsRequired() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("ExtraProperties") .HasColumnName("ExtraProperties") @@ -162,7 +160,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") .HasColumnName("CreationTime") @@ -435,7 +434,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("CreationTime") .HasColumnName("CreationTime") @@ -592,7 +592,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("Content") .IsRequired() @@ -690,7 +691,8 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnName("ConcurrencyStamp") - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); b.Property("DefaultDocumentName") .IsRequired() diff --git a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj index 9da16c65cf..2e06315603 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj +++ b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/GetAllInput.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/GetAllInput.cs index e375f6ba0b..1512c13da9 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/GetAllInput.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/GetAllInput.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; using Volo.Docs.Documents; namespace Volo.Docs.Admin.Documents @@ -9,19 +10,19 @@ namespace Volo.Docs.Admin.Documents { public Guid? ProjectId { get; set; } - [StringLength(DocumentConsts.MaxNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(DocumentConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxVersionNameLength))] public string Version { get; set; } - [StringLength(DocumentConsts.MaxLanguageCodeNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxLanguageCodeNameLength))] public string LanguageCode { get; set; } - [StringLength(DocumentConsts.MaxFileNameNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxFileNameNameLength))] public string FileName { get; set; } - [StringLength(DocumentConsts.MaxFormatNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxFormatNameLength))] public string Format { get; set; } public DateTime? CreationTimeMin { get; set; } diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullAllDocumentInput.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullAllDocumentInput.cs index d796c00e64..c375de8b67 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullAllDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullAllDocumentInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Documents; namespace Volo.Docs.Admin.Documents @@ -8,10 +9,10 @@ namespace Volo.Docs.Admin.Documents { public Guid ProjectId { get; set; } - [StringLength(DocumentConsts.MaxLanguageCodeNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxLanguageCodeNameLength))] public string LanguageCode { get; set; } - [StringLength(DocumentConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxVersionNameLength))] public string Version { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullDocumentInput.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullDocumentInput.cs index fcc9816b02..dce5de0420 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullDocumentInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Documents; namespace Volo.Docs.Admin.Documents @@ -8,13 +9,13 @@ namespace Volo.Docs.Admin.Documents { public Guid ProjectId { get; set; } - [StringLength(DocumentConsts.MaxNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(DocumentConsts.MaxLanguageCodeNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxLanguageCodeNameLength))] public string LanguageCode { get; set; } - [StringLength(DocumentConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxVersionNameLength))] public string Version { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj index 076bc1d37a..00b0e26d75 100644 --- a/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj +++ b/modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj @@ -4,7 +4,7 @@ - netcoreapp3.1 + netstandard2.0 Volo.Docs.Admin.HttpApi.Client Volo.Docs.Admin.HttpApi.Client diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Create.cshtml.cs b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Create.cshtml.cs index e6de68a3b1..506b674ba5 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Create.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Create.cshtml.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Validation; using Volo.Docs.Admin.Projects; using Volo.Docs.Projects; @@ -70,11 +71,11 @@ namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects public abstract class CreateProjectViewModelBase { [Required] - [StringLength(ProjectConsts.MaxNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxNameLength))] public string Name { get; set; } [Required] - [StringLength(ProjectConsts.MaxShortNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxShortNameLength))] [InputInfoText("ShortNameInfoText")] public string ShortName { get; set; } @@ -82,21 +83,21 @@ namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects [SelectItems(nameof(FormatTypes))] public string Format { get; set; } - [StringLength(ProjectConsts.MaxDefaultDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxDefaultDocumentNameLength))] public string DefaultDocumentName { get; set; } = "Index"; - [StringLength(ProjectConsts.MaxNavigationDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxNavigationDocumentNameLength))] public string NavigationDocumentName { get; set; } = "docs-nav.json"; - [StringLength(ProjectConsts.MaxParametersDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxParametersDocumentNameLength))] public string ParametersDocumentName { get; set; } = "docs-params.json"; - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string MinimumVersion { get; set; } public string MainWebsiteUrl { get; set; } - [StringLength(ProjectConsts.MaxLatestVersionBranchNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxLatestVersionBranchNameLength))] public string LatestVersionBranchName { get; set; } [HiddenInput] diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs index 3b9aba5303..ef1762c460 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Validation; using Volo.Docs.Admin.Projects; using Volo.Docs.Projects; @@ -83,28 +84,28 @@ namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects public Guid Id { get; set; } [Required] - [StringLength(ProjectConsts.MaxNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxNameLength))] public string Name { get; set; } [Required] [SelectItems(nameof(FormatTypes))] public string Format { get; set; } - [StringLength(ProjectConsts.MaxDefaultDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxDefaultDocumentNameLength))] public string DefaultDocumentName { get; set; } - [StringLength(ProjectConsts.MaxNavigationDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxNavigationDocumentNameLength))] public string NavigationDocumentName { get; set; } - [StringLength(ProjectConsts.MaxParametersDocumentNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxParametersDocumentNameLength))] public string ParametersDocumentName { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string MinimumVersion { get; set; } public string MainWebsiteUrl { get; set; } - [StringLength(ProjectConsts.MaxLatestVersionBranchNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxLatestVersionBranchNameLength))] public string LatestVersionBranchName { get; set; } } diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Pull.cshtml.cs b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Pull.cshtml.cs index 195337a22e..5cbd094834 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Pull.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Pull.cshtml.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; using Volo.Docs.Admin.Documents; using Volo.Docs.Admin.Projects; using Volo.Docs.Documents; @@ -60,15 +61,15 @@ namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects public bool All { get; set; } [Required] - [StringLength(DocumentConsts.MaxNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxNameLength))] public string Name { get; set; } [Required] - [StringLength(DocumentConsts.MaxLanguageCodeNameLength)] + [DynamicStringLength(typeof(DocumentConsts),nameof(DocumentConsts.MaxLanguageCodeNameLength))] public string LanguageCode { get; set; } [Required] - [StringLength(DocumentConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxVersionNameLength))] public string Version { get; set; } } } diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json b/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json deleted file mode 100644 index b0ed7371d0..0000000000 --- a/modules/docs/src/Volo.Docs.Admin.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51397", - "sslPort": 44346 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Docs.Admin.Web": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj index 164c28cdb2..5b75a24f41 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj +++ b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj @@ -30,11 +30,9 @@ - - diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDefaultDocumentInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDefaultDocumentInput.cs index 3deeb83967..1cef038512 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDefaultDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDefaultDocumentInput.cs @@ -1,5 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; +using System.Runtime.CompilerServices; +using Volo.Abp.Validation; using Volo.Docs.Language; using Volo.Docs.Projects; @@ -9,11 +11,11 @@ namespace Volo.Docs.Documents { public Guid ProjectId { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string Version { get; set; } [Required] - [StringLength(LanguageConsts.MaxLanguageCodeLength)] + [DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxLanguageCodeLength))] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentInput.cs index 412f41b82e..3646f2ade0 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Language; using Volo.Docs.Projects; @@ -10,14 +11,14 @@ namespace Volo.Docs.Documents public Guid ProjectId { get; set; } [Required] - [StringLength(DocumentConsts.MaxNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string Version { get; set; } [Required] - [StringLength(LanguageConsts.MaxLanguageCodeLength)] + [DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxLanguageCodeLength))] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentResourceInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentResourceInput.cs index 6f759c47d8..52c1cf78af 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentResourceInput.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentResourceInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Language; using Volo.Docs.Projects; @@ -10,14 +11,14 @@ namespace Volo.Docs.Documents public Guid ProjectId { get; set; } [Required] - [StringLength(DocumentConsts.MaxNameLength)] + [DynamicStringLength(typeof(DocumentConsts), nameof(DocumentConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string Version { get; set; } [Required] - [StringLength(LanguageConsts.MaxLanguageCodeLength)] + [DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxLanguageCodeLength))] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetNavigationDocumentInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetNavigationDocumentInput.cs index 6866c8c633..ee4e3380ee 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetNavigationDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetNavigationDocumentInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Language; using Volo.Docs.Projects; @@ -9,11 +10,11 @@ namespace Volo.Docs.Documents { public Guid ProjectId { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string Version { get; set; } [Required] - [StringLength(LanguageConsts.MaxLanguageCodeLength)] + [DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxLanguageCodeLength))] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetParametersDocumentInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetParametersDocumentInput.cs index 33bd25b394..1e277f36a3 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetParametersDocumentInput.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetParametersDocumentInput.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; using Volo.Docs.Language; using Volo.Docs.Projects; @@ -9,11 +10,11 @@ namespace Volo.Docs.Documents { public Guid ProjectId { get; set; } - [StringLength(ProjectConsts.MaxVersionNameLength)] + [DynamicStringLength(typeof(ProjectConsts), nameof(ProjectConsts.MaxVersionNameLength))] public string Version { get; set; } [Required] - [StringLength(LanguageConsts.MaxLanguageCodeLength)] + [DynamicStringLength(typeof(LanguageConsts), nameof(LanguageConsts.MaxLanguageCodeLength))] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentConsts.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentConsts.cs index f629abd0a4..04e3de3fd5 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentConsts.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentConsts.cs @@ -2,14 +2,49 @@ { public static class DocumentConsts { - public const int MaxNameLength = 255; - public const int MaxVersionNameLength = 128; - public const int MaxLanguageCodeNameLength = 128; - public const int MaxFileNameNameLength = 128; - public const int MaxFormatNameLength = 128; - public const int MaxEditLinkLength = 2048; - public const int MaxRootUrlLength = 2048; - public const int MaxRawRootUrlLength = 2048; - public const int MaxLocalDirectoryLength = 512; + /// + /// Default value: 255 + /// + public static int MaxNameLength { get; set; } = 255; + + /// + /// Default value: 128 + /// + public static int MaxVersionNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxLanguageCodeNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxFileNameNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxFormatNameLength { get; set; } = 128; + + /// + /// Default value: 2048 + /// + public static int MaxEditLinkLength { get; set; } = 2048; + + /// + /// Default value: 2048 + /// + public static int MaxRootUrlLength { get; set; } = 2048; + + /// + /// Default value: 2048 + /// + public static int MaxRawRootUrlLength { get; set; } = 2048; + + /// + /// Default value: 512 + /// + public static int MaxLocalDirectoryLength { get; set; } = 512; } } diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Language/LanguageConsts.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Language/LanguageConsts.cs index 867cc2dd4e..cd449a37fd 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Language/LanguageConsts.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Language/LanguageConsts.cs @@ -3,6 +3,9 @@ namespace Volo.Docs.Language { public static class LanguageConsts { - public const int MaxLanguageCodeLength = 10; + /// + /// Default value: 10 + /// + public static int MaxLanguageCodeLength { get; set; } = 10; } } diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs index 927a1a4c59..3322fb7732 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs @@ -2,12 +2,39 @@ { public static class ProjectConsts { - public const int MaxNameLength = 128; - public const int MaxShortNameLength = 32; - public const int MaxDefaultDocumentNameLength = 128; - public const int MaxNavigationDocumentNameLength = 128; - public const int MaxParametersDocumentNameLength = 128; - public const int MaxLatestVersionBranchNameLength = 128; - public const int MaxVersionNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxNameLength { get; set; } = 128; + + /// + /// Default value: 32 + /// + public static int MaxShortNameLength { get; set; } = 32; + + /// + /// Default value: 128 + /// + public static int MaxDefaultDocumentNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxNavigationDocumentNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxParametersDocumentNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxLatestVersionBranchNameLength { get; set; } = 128; + + /// + /// Default value: 128 + /// + public static int MaxVersionNameLength { get; set; } = 128; } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj index 5331433e24..5b93ecdf8a 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj +++ b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index ef6f6e1ebb..1da1ab1f9d 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -38,7 +38,7 @@ namespace Volo.Docs.GitHub.Documents var rawDocumentUrl = rawRootUrl + documentName; var isNavigationDocument = documentName == project.NavigationDocumentName; var isParameterDocument = documentName == project.ParametersDocumentName; - var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/") + languageCode + "/" + documentName; + var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/").EnsureEndsWith('/') + languageCode + "/" + documentName; var localDirectory = ""; var fileName = documentName; diff --git a/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json b/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json deleted file mode 100644 index e32f163034..0000000000 --- a/modules/docs/src/Volo.Docs.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55007/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Docs.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55020/" - } - } -} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj index 013435afda..4479ec40af 100644 --- a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj +++ b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj @@ -21,8 +21,8 @@ - - + + @@ -33,11 +33,9 @@ - - diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj index 91eb42aaf2..a53c5606a3 100644 --- a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj index 2e4fcd6402..c5a1a2d642 100644 --- a/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj +++ b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj index 5483881192..6c6af318cb 100644 --- a/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj index b41999464c..7774d2b76a 100644 --- a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj +++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo.Docs.MongoDB.Tests.csproj b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo.Docs.MongoDB.Tests.csproj index dac64c24c1..57ef225881 100644 --- a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo.Docs.MongoDB.Tests.csproj +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo.Docs.MongoDB.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj index 7a19db874e..4a9523b667 100644 --- a/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj +++ b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj @@ -6,12 +6,12 @@ - - + + - + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs index 749805da05..c941a1b7eb 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs @@ -2,6 +2,6 @@ { public class FeatureManagementRemoteServiceConsts { - public const string RemoteServiceName = "FeatureManagement"; + public const string RemoteServiceName = "AbpFeatureManagement"; } } \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs index 24b04b7013..6c9db9bade 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs @@ -16,7 +16,6 @@ namespace Volo.Abp.FeatureManagement public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppService { protected FeatureManagementOptions Options { get; } - protected IFeatureManager FeatureManager { get; } protected IFeatureDefinitionManager FeatureDefinitionManager { get; } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs index b35225edb2..26388fdaf9 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs @@ -1,6 +1,9 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; using Volo.Abp.Guids; namespace Volo.Abp.FeatureManagement @@ -8,17 +11,20 @@ namespace Volo.Abp.FeatureManagement public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency { protected IDistributedCache Cache { get; } + protected IFeatureDefinitionManager FeatureDefinitionManager { get; } protected IFeatureValueRepository FeatureValueRepository { get; } protected IGuidGenerator GuidGenerator { get; } public FeatureManagementStore( IFeatureValueRepository featureValueRepository, IGuidGenerator guidGenerator, - IDistributedCache cache) + IDistributedCache cache, + IFeatureDefinitionManager featureDefinitionManager) { FeatureValueRepository = featureValueRepository; GuidGenerator = guidGenerator; Cache = cache; + FeatureDefinitionManager = featureDefinitionManager; } public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) @@ -61,16 +67,43 @@ namespace Volo.Abp.FeatureManagement return cacheItem; } - var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); + cacheItem = new FeatureValueCacheItem(null); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); - cacheItem = new FeatureValueCacheItem(featureValue?.Value); + return cacheItem; + } + + private async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + FeatureValueCacheItem currentCacheItem) + { + var featureDefinitions = FeatureDefinitionManager.GetAll(); + var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var featureDefinition in featureDefinitions) + { + var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name); + + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(featureDefinition.Name, providerName, providerKey), + new FeatureValueCacheItem(featureValue) + ) + ); - await Cache.SetAsync( - cacheKey, - cacheItem - ); + if (featureDefinition.Name == currentName) + { + currentCacheItem.Value = featureValue; + } + } - return cacheItem; + await Cache.SetManyAsync(cacheItems); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs index 0fd1a644fa..f44deca44a 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs @@ -2,12 +2,24 @@ { public static class FeatureValueConsts { - public const int MaxNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxNameLength { get; set;} = 128; - public const int MaxProviderNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxProviderNameLength { get; set;} = 64; - public const int MaxProviderKeyLength = 64; + /// + /// Default value: 64 + /// + public static int MaxProviderKeyLength { get; set;} = 64; - public const int MaxValueLength = 128; + /// + /// Default value: 128 + /// + public static int MaxValueLength { get; set;} = 128; } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs index e3eda46033..0ae78ed03f 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs @@ -5,7 +5,8 @@ using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.FeatureManagement { [RemoteService(Name = FeatureManagementRemoteServiceConsts.RemoteServiceName)] - [Area("abp")] + [Area("featureManagement")] + [Route("api/feature-management/features")] public class FeaturesController : AbpController, IFeatureAppService { protected IFeatureAppService FeatureAppService { get; } @@ -15,11 +16,13 @@ namespace Volo.Abp.FeatureManagement FeatureAppService = featureAppService; } + [HttpGet] public virtual Task GetAsync(string providerName, string providerKey) { return FeatureAppService.GetAsync(providerName, providerKey); } + [HttpPut] public virtual Task UpdateAsync(string providerName, string providerKey, UpdateFeaturesDto input) { return FeatureAppService.UpdateAsync(providerName, providerKey, input); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 23a4efbb61..0000000000 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56993/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Abp.FeatureManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:56994/" - } - } -} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj index 19124ccf85..f66045d616 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo.Abp.FeatureManagement.Application.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj index 95793ffff4..e0dcbc8504 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo.Abp.FeatureManagement.Domain.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj index 31d4a3065f..4feee0b39d 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj index 91ccb18368..13becca10b 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo.Abp.FeatureManagement.MongoDB.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj index 111643ce37..8d62c99864 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo.Abp.FeatureManagement.TestBase.csproj @@ -14,12 +14,12 @@ - - + + - + diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs index 8e11d89b30..491e82d766 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityRoleCreateOrUpdateDtoBase.cs @@ -1,12 +1,13 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityRoleCreateOrUpdateDtoBase : ExtensibleObject { [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] public string Name { get; set; } public bool IsDefault { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs index a572c7db2c..a080a7e33e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateDto.cs @@ -1,13 +1,14 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityUserCreateDto : IdentityUserCreateOrUpdateDtoBase { - [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DisableAuditing] + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs index c18f2c16b5..e06ea8b987 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs @@ -2,27 +2,28 @@ using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public abstract class IdentityUserCreateOrUpdateDtoBase : ExtensibleObject { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs index 216a55df9e..aa499bf2cd 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserUpdateDto.cs @@ -1,13 +1,14 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class IdentityUserUpdateDto : IdentityUserCreateOrUpdateDtoBase, IHasConcurrencyStamp { - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DisableAuditing] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } public string ConcurrencyStamp { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs index 52919c7744..5e0397bdfa 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UpdateProfileDto.cs @@ -1,23 +1,24 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.Identity { public class UpdateProfileDto : ExtensibleObject { - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json deleted file mode 100644 index fc5b1a72f7..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53901/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.AspNetCore": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53902/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs index 388c0849b7..c0e3031568 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs @@ -9,13 +9,17 @@ namespace Volo.Abp.Identity.AspNetCore )] public class AbpIdentityAspNetCoreModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services - .GetObject() - .AddDefaultTokenProviders() - .AddSignInManager(); + PreConfigure(builder => + { + builder.AddDefaultTokenProviders(); + builder.AddSignInManager(); + }); + } + public override void ConfigureServices(ServiceConfigurationContext context) + { //(TODO: Extract an extension method like IdentityBuilder.AddAbpSecurityStampValidator()) context.Services.AddScoped(); context.Services.AddScoped(typeof(SecurityStampValidator), provider => provider.GetService(typeof(AbpSecurityStampValidator))); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs index 32e3197e12..eafd0ed135 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs @@ -2,10 +2,24 @@ { public class IdentityClaimTypeConsts { - public const int MaxNameLength = 256; - public const int MaxRegexLength = 512; - public const int MaxRegexDescriptionLength = 128; - public const int MaxDescriptionLength = 256; - public const int MaxConcurrencyStampLength = 256; + /// + /// Default value: 256 + /// + public static int MaxNameLength { get; set; } = 256; + + /// + /// Default value: 512 + /// + public static int MaxRegexLength { get; set; } = 512; + + /// + /// Default value: 128 + /// + public static int MaxRegexDescriptionLength { get; set; } = 128; + + /// + /// Default value: 256 + /// + public static int MaxDescriptionLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs index f53717486f..0f20ac40ef 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleClaimConsts.cs @@ -2,8 +2,8 @@ { public static class IdentityRoleClaimConsts { - public const int MaxClaimTypeLength = IdentityUserClaimConsts.MaxClaimTypeLength; + public static int MaxClaimTypeLength { get; set; } = IdentityUserClaimConsts.MaxClaimTypeLength; - public const int MaxClaimValueLength = IdentityUserClaimConsts.MaxClaimValueLength; + public static int MaxClaimValueLength { get; set; } = IdentityUserClaimConsts.MaxClaimValueLength; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs index cac33aa53d..f51be5da8f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs @@ -2,8 +2,14 @@ { public static class IdentityRoleConsts { - public const int MaxNameLength = 256; - public const int MaxNormalizedNameLength = MaxNameLength; - public const int MaxConcurrencyStampLength = 256; + /// + /// Default value: 256 + /// + public static int MaxNameLength { get; set; } = 256; + + /// + /// Default value: 256 + /// + public static int MaxNormalizedNameLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs index 025b255f11..84ba493853 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs @@ -2,8 +2,14 @@ { public static class IdentityUserClaimConsts { - public const int MaxClaimTypeLength = 256; + /// + /// Default value: 256 + /// + public static int MaxClaimTypeLength { get; set; } = 256; - public const int MaxClaimValueLength = 1024; + /// + /// Default value: 1024 + /// + public static int MaxClaimValueLength { get; set; } = 1024; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs index ef2cc57ef4..c374cbc678 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs @@ -4,26 +4,33 @@ namespace Volo.Abp.Identity { public static class IdentityUserConsts { - public const int MaxUserNameLength = AbpUserConsts.MaxUserNameLength; + public static int MaxUserNameLength { get; set; } = AbpUserConsts.MaxUserNameLength; - public const int MaxNameLength = AbpUserConsts.MaxNameLength; + public static int MaxNameLength { get; set; } = AbpUserConsts.MaxNameLength; - public const int MaxSurnameLength = AbpUserConsts.MaxSurnameLength; + public static int MaxSurnameLength { get; set; } = AbpUserConsts.MaxSurnameLength; - public const int MaxNormalizedUserNameLength = MaxUserNameLength; + public static int MaxNormalizedUserNameLength { get; set; } = MaxUserNameLength; - public const int MaxEmailLength = AbpUserConsts.MaxEmailLength; + public static int MaxEmailLength { get; set; } = AbpUserConsts.MaxEmailLength; - public const int MaxNormalizedEmailLength = MaxEmailLength; + public static int MaxNormalizedEmailLength { get; set; } = MaxEmailLength; - public const int MaxPhoneNumberLength = AbpUserConsts.MaxPhoneNumberLength; + public static int MaxPhoneNumberLength { get; set; } = AbpUserConsts.MaxPhoneNumberLength; - public const int MaxPasswordLength = 128; + /// + /// Default value: 128 + /// + public static int MaxPasswordLength { get; set; } = 128; - public const int MaxPasswordHashLength = 256; + /// + /// Default value: 256 + /// + public static int MaxPasswordHashLength { get; set; } = 256; - public const int MaxSecurityStampLength = 256; - - public const int MaxConcurrencyStampLength = 256; + /// + /// Default value: 256 + /// + public static int MaxSecurityStampLength { get; set; } = 256; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs index 0ef43bb1b1..8435221fd3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs @@ -2,8 +2,19 @@ { public static class IdentityUserLoginConsts { - public const int MaxLoginProviderLength = 64; - public const int MaxProviderKeyLength = 196; - public const int MaxProviderDisplayNameLength = 128; + /// + /// Default value: 64 + /// + public static int MaxLoginProviderLength { get; set; } = 64; + + /// + /// Default value: 196 + /// + public static int MaxProviderKeyLength { get; set; } = 196; + + /// + /// Default value: 128 + /// + public static int MaxProviderDisplayNameLength { get; set; } = 128; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs index 92305851fb..a585a8383b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs @@ -2,8 +2,14 @@ { public static class IdentityUserTokenConsts { - public const int MaxLoginProviderLength = 64; + /// + /// Default value: 64 + /// + public static int MaxLoginProviderLength { get; set; } = 64; - public const int MaxNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxNameLength { get; set; } = 128; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs index ad6091a44e..7338fc8a28 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs @@ -5,7 +5,7 @@ /// /// Maximum length of the property. /// - public const int MaxDisplayNameLength = 128; + public static int MaxDisplayNameLength { get; set; } = 128; /// /// Maximum depth of an OU hierarchy. diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs new file mode 100644 index 0000000000..09013aa1ce --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitEto.cs @@ -0,0 +1,16 @@ +using System; + +namespace Volo.Abp.Identity +{ + [Serializable] + public class OrganizationUnitEto + { + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public string Code { get; set; } + + public string DisplayName { get; set; } + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs index 3892fb00f6..a8e488871a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConfiguration.cs @@ -31,5 +31,14 @@ namespace Volo.Abp.ObjectExtending configureAction ); } + + public IdentityModuleExtensionConfiguration ConfigureOrganizationUnit( + Action configureAction) + { + return this.ConfigureEntity( + IdentityModuleExtensionConsts.EntityNames.OrganizationUnit, + configureAction + ); + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs index 6d2b4c0653..45c9db6462 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/ObjectExtending/IdentityModuleExtensionConsts.cs @@ -11,6 +11,8 @@ public const string Role = "Role"; public const string ClaimType = "ClaimType"; + + public const string OrganizationUnit = "OrganizationUnit"; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj index fa22210db0..7712adc0a9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj @@ -30,7 +30,7 @@ - + diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index 07c4d2e971..d21e945f60 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -34,6 +34,7 @@ namespace Volo.Abp.Identity options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); + options.EtoMappings.Add(typeof(AbpIdentityDomainModule)); }); var identityBuilder = context.Services.AddAbpIdentity(options => @@ -66,6 +67,12 @@ namespace Volo.Abp.Identity IdentityModuleExtensionConsts.EntityNames.ClaimType, typeof(IdentityClaimType) ); + + ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity( + IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.OrganizationUnit, + typeof(OrganizationUnit) + ); } private static void AddAbpIdentityOptionsFactory(IServiceCollection services) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs index 547525b46e..9a5557401d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.Identity CreateMap(); CreateMap(); CreateMap(); + CreateMap(); } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index 74cc2ce995..000a2a8f00 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -126,9 +126,6 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.Property(r => r.Name).IsRequired().HasMaxLength(IdentityRoleConsts.MaxNameLength); b.Property(r => r.NormalizedName).IsRequired().HasMaxLength(IdentityRoleConsts.MaxNormalizedNameLength); - b.Property(u => u.ConcurrencyStamp).IsRequired().IsConcurrencyToken() - .HasMaxLength(IdentityRoleConsts.MaxConcurrencyStampLength) - .HasColumnName(nameof(IdentityRole.ConcurrencyStamp)); b.Property(r => r.IsDefault).HasColumnName(nameof(IdentityRole.IsDefault)); b.Property(r => r.IsStatic).HasColumnName(nameof(IdentityRole.IsStatic)); b.Property(r => r.IsPublic).HasColumnName(nameof(IdentityRole.IsPublic)); @@ -163,9 +160,6 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.Property(uc => uc.Regex).HasMaxLength(IdentityClaimTypeConsts.MaxRegexLength); b.Property(uc => uc.RegexDescription).HasMaxLength(IdentityClaimTypeConsts.MaxRegexDescriptionLength); b.Property(uc => uc.Description).HasMaxLength(IdentityClaimTypeConsts.MaxDescriptionLength); - b.Property(uc => uc.ConcurrencyStamp).IsRequired().IsConcurrencyToken() - .HasMaxLength(IdentityClaimTypeConsts.MaxConcurrencyStampLength) - .HasColumnName(nameof(IdentityClaimType.ConcurrencyStamp)); }); builder.Entity(b => diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json deleted file mode 100644 index 6ba85345ab..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53978/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.HttpApi.Client": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53980/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json deleted file mode 100644 index 2323768a18..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53977/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.HttpApi": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53979/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs index e7244c80f1..df86acd94d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles { @@ -34,7 +35,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles public class RoleInfoModel { [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] [Display(Name = "DisplayName:RoleName")] public string Name { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs index 69305d243c..8a004e95a5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Roles { @@ -44,7 +45,7 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Roles public string ConcurrencyStamp { get; set; } [Required] - [StringLength(IdentityRoleConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityRoleConsts), nameof(IdentityRoleConsts.MaxNameLength))] [Display(Name = "DisplayName:RoleName")] public string Name { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs index e7a1f4a26d..6149c676b8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users { @@ -54,27 +55,27 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users public class UserInfoViewModel { [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxPasswordLength)] [DataType(DataType.Password)] [DisableAuditing] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string Password { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } = true; diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs index f14ec21fe6..08c5ae0dd9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Volo.Abp.Auditing; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; namespace Volo.Abp.Identity.Web.Pages.Identity.Users { @@ -63,26 +64,26 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users public string ConcurrencyStamp { get; set; } [Required] - [StringLength(IdentityUserConsts.MaxUserNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] public string UserName { get; set; } - [StringLength(IdentityUserConsts.MaxNameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxNameLength))] public string Name { get; set; } - [StringLength(IdentityUserConsts.MaxSurnameLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxSurnameLength))] public string Surname { get; set; } - [StringLength(IdentityUserConsts.MaxPasswordLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] [DataType(DataType.Password)] [DisableAuditing] public string Password { get; set; } [Required] [EmailAddress] - [StringLength(IdentityUserConsts.MaxEmailLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] public string Email { get; set; } - [StringLength(IdentityUserConsts.MaxPhoneNumberLength)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] public string PhoneNumber { get; set; } public bool TwoFactorEnabled { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json b/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json deleted file mode 100644 index 9a8b023b55..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55588/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.Identity.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55590/" - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index f5c9ffc3d4..5a8367dc5f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -25,9 +25,7 @@ - - diff --git a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo.Abp.Identity.Application.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo.Abp.Identity.Application.Tests.csproj index b86bcde846..51ff7f612c 100644 --- a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo.Abp.Identity.Application.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo.Abp.Identity.Application.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj index 731a1eeab1..dbce36b3c9 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj index 81f0f63e94..9e31625c1b 100644 --- a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo.Abp.Identity.MongoDB.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo.Abp.Identity.MongoDB.Tests.csproj index 73d1cbf32e..dcd19f08b7 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo.Abp.Identity.MongoDB.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo.Abp.Identity.MongoDB.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo.Abp.Identity.TestBase.csproj b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo.Abp.Identity.TestBase.csproj index 0dd175fabc..10ccec2a11 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo.Abp.Identity.TestBase.csproj +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo.Abp.Identity.TestBase.csproj @@ -19,12 +19,12 @@ - - + + - + diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientIdPRestrictionConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientIdPRestrictionConsts.cs index c71301871a..b238a01935 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientIdPRestrictionConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientIdPRestrictionConsts.cs @@ -2,6 +2,9 @@ { public class ClientIdPRestrictionConsts { - public const int ProviderMaxLength = 200; + /// + /// Default value: 200 + /// + public static int ProviderMaxLength { get; set; } = 200; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs index 3092232d47..7d4764e1a3 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs @@ -2,7 +2,10 @@ { public class ClientPostLogoutRedirectUriConsts { - public const int PostLogoutRedirectUriMaxLength = 2000; + /// + /// Default value: 200 + /// + public static int PostLogoutRedirectUriMaxLength { get; set; } = 2000; public static int PostLogoutRedirectUriMaxLengthValue { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPropertyConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPropertyConsts.cs index 6535304596..664e1076b0 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPropertyConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPropertyConsts.cs @@ -2,7 +2,14 @@ { public class ClientPropertyConsts { - public const int KeyMaxLength = 250; - public const int ValueMaxLength = 2000; + /// + /// Default value: 250 + /// + public static int KeyMaxLength { get; set; } = 250; + + /// + /// Default value: 2000 + /// + public static int ValueMaxLength { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs index e972388487..7650a3d952 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs @@ -2,7 +2,10 @@ { public class ClientRedirectUriConsts { - public const int RedirectUriMaxLength = 2000; + /// + /// Default value: 2000 + /// + public static int RedirectUriMaxLength { get; set; } = 2000; public static int RedirectUriMaxLengthValue { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientScopeConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientScopeConsts.cs index 6a5b62e692..0ec1c04595 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientScopeConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientScopeConsts.cs @@ -2,6 +2,9 @@ { public class ClientScopeConsts { - public const int ScopeMaxLength = 200; + /// + /// Default value: 200 + /// + public static int ScopeMaxLength { get; set; } = 200; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs index 6d0836ab17..b1dd21d6f8 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs @@ -2,11 +2,34 @@ { public class PersistedGrantConsts { - public const int KeyMaxLength = 200; - public const int TypeMaxLength = 50; - public const int SubjectIdMaxLength = 200; - public const int ClientIdMaxLength = 200; - public const int DataMaxLength = 50000; - public static int DataMaxLengthValue { get; set; }= 50000; + /// + /// Default value: 200 + /// + public static int KeyMaxLength { get; set; } = 200; + + /// + /// Default value: 50 + /// + public static int TypeMaxLength { get; set; } = 50; + + /// + /// Default value: 200 + /// + public static int SubjectIdMaxLength { get; set; } = 200; + + /// + /// Default value: 200 + /// + public static int ClientIdMaxLength { get; set; } = 200; + + /// + /// Default value: 50000 + /// + public static int DataMaxLength { get; set; } = 50000; + + /// + /// Default value: 50000 + /// + public static int DataMaxLengthValue { get; set; } = 50000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs index c60cc34ad8..a7c713e161 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs @@ -2,8 +2,19 @@ { public class IdentityResourceConsts { - public const int NameMaxLength = 200; - public const int DisplayNameMaxLength = 200; - public const int DescriptionMaxLength = 1000; + /// + /// Default value: 200 + /// + public static int NameMaxLength { get; set; } = 200; + + /// + /// Default value: 200 + /// + public static int DisplayNameMaxLength { get; set; } = 200; + + /// + /// Default value: 200 + /// + public static int DescriptionMaxLength { get; set; } = 1000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs index 76635e322e..156088a97b 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs @@ -2,9 +2,21 @@ { public class SecretConsts { - public const int TypeMaxLength = 250; - public const int ValueMaxLength = 4000; + /// + /// Default value: 250 + /// + public static int TypeMaxLength { get; set; } = 250; + + /// + /// Default value: 4000 + /// + public static int ValueMaxLength { get; set; } = 4000; + public static int ValueMaxLengthValue { get; set; } = ValueMaxLength; - public const int DescriptionMaxLength = 2000; + + /// + /// Default value: 2000 + /// + public static int DescriptionMaxLength { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/UserClaimConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/UserClaimConsts.cs index 9133482f1f..cb0279fd2b 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/UserClaimConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/UserClaimConsts.cs @@ -2,6 +2,9 @@ { public class UserClaimConsts { - public const int TypeMaxLength = 200; + /// + /// Default value: 200 + /// + public static int TypeMaxLength { get; set; } = 200; } } \ No newline at end of file diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj index e90296c8ac..6f62f7d56b 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo.Abp.IdentityServer.Domain.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj index 74e1c5867f..b91a1ea9f6 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj @@ -19,12 +19,12 @@ - - + + - + diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj index 65e2a1f5c9..789d512c5f 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo.Abp.IdentityServer.MongoDB.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo.Abp.IdentityServer.TestBase.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo.Abp.IdentityServer.TestBase.csproj index d6a6ee9a0c..e9a3d38c8c 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo.Abp.IdentityServer.TestBase.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo.Abp.IdentityServer.TestBase.csproj @@ -19,12 +19,12 @@ - - + + - + diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs index 8d579638ec..5cb2397349 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs @@ -2,10 +2,19 @@ { public static class PermissionGrantConsts { - public const int MaxNameLength = 128; + /// + /// Default value: 128 + /// + public static int MaxNameLength { get; set; } = 128; - public const int MaxProviderNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxProviderNameLength { get; set; } = 64; - public const int MaxProviderKeyLength = 64; + /// + /// Default value: 64 + /// + public static int MaxProviderKeyLength { get; set; } = 64; } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs index e4d3f1648e..49013954a2 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGrantCacheItem.cs @@ -5,8 +5,6 @@ namespace Volo.Abp.PermissionManagement [Serializable] public class PermissionGrantCacheItem { - public string Name { get; set; } //TODO: Consider to remove this - public bool IsGranted { get; set; } public PermissionGrantCacheItem() @@ -14,9 +12,8 @@ namespace Volo.Abp.PermissionManagement } - public PermissionGrantCacheItem(string name, bool isGranted) + public PermissionGrantCacheItem(bool isGranted) { - Name = name; IsGranted = isGranted; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs index 2ffd6f80e1..bdfdc38881 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Authorization.Permissions; @@ -10,17 +12,21 @@ namespace Volo.Abp.PermissionManagement public class PermissionStore : IPermissionStore, ITransientDependency { public ILogger Logger { get; set; } - + protected IPermissionGrantRepository PermissionGrantRepository { get; } + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + protected IDistributedCache Cache { get; } - + public PermissionStore( IPermissionGrantRepository permissionGrantRepository, - IDistributedCache cache) + IDistributedCache cache, + IPermissionDefinitionManager permissionDefinitionManager) { PermissionGrantRepository = permissionGrantRepository; Cache = cache; + PermissionDefinitionManager = permissionDefinitionManager; Logger = NullLogger.Instance; } @@ -29,7 +35,10 @@ namespace Volo.Abp.PermissionManagement return (await GetCacheItemAsync(name, providerName, providerKey)).IsGranted; } - protected virtual async Task GetCacheItemAsync(string name, string providerName, string providerKey) + protected virtual async Task GetCacheItemAsync( + string name, + string providerName, + string providerKey) { var cacheKey = CalculateCacheKey(name, providerName, providerKey); @@ -43,23 +52,51 @@ namespace Volo.Abp.PermissionManagement return cacheItem; } - Logger.LogDebug($"Not found in the cache, getting from the repository: {cacheKey}"); + Logger.LogDebug($"Not found in the cache: {cacheKey}"); + + cacheItem = new PermissionGrantCacheItem(false); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); - cacheItem = new PermissionGrantCacheItem( - name, - (await PermissionGrantRepository.FindAsync(name, providerName, providerKey)) != null - ); + return cacheItem; + } - Logger.LogDebug($"Setting the cache item: {cacheKey}"); + protected virtual async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + PermissionGrantCacheItem currentCacheItem) + { + var permissions = PermissionDefinitionManager.GetPermissions(); + + Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}"); - await Cache.SetAsync( - cacheKey, - cacheItem + var grantedPermissionsHashSet = new HashSet( + (await PermissionGrantRepository.GetListAsync(providerName, providerKey)).Select(p => p.Name) ); - Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); - return cacheItem; + var cacheItems = new List>(); + + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, providerName, providerKey), + new PermissionGrantCacheItem(isGranted)) + ); + + if (permission.Name == currentName) + { + currentCacheItem.IsGranted = isGranted; + } + } + + await Cache.SetManyAsync(cacheItems); + + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) @@ -67,4 +104,4 @@ namespace Volo.Abp.PermissionManagement return PermissionGrantCacheItem.CalculateCacheKey(name, providerName, providerKey); } } -} +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs index 2ed9a4d044..bb9db51f98 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs @@ -5,7 +5,8 @@ using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.PermissionManagement { [RemoteService(Name = PermissionManagementRemoteServiceConsts.RemoteServiceName)] - [Area("abp")] + [Area("permissionManagement")] + [Route("api/permission-management/permissions")] public class PermissionsController : AbpController, IPermissionAppService { protected IPermissionAppService PermissionAppService { get; } @@ -15,11 +16,13 @@ namespace Volo.Abp.PermissionManagement PermissionAppService = permissionAppService; } + [HttpGet] public virtual Task GetAsync(string providerName, string providerKey) { return PermissionAppService.GetAsync(providerName, providerKey); } + [HttpPut] public virtual Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input) { return PermissionAppService.UpdateAsync(providerName, providerKey, input); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 6cb3381b6c..0000000000 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55926/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.PermissionManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55927/" - } - } -} \ No newline at end of file diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj index 178f755a99..647ba0f256 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo.Abp.PermissionManagement.Application.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj index db520434e9..c22762d006 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj @@ -17,8 +17,8 @@ - - + + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj index a668fb9d6f..5fe2086c6c 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj @@ -17,8 +17,8 @@ - - + + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj index 2e688fd5e6..4fe8e904c9 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo.Abp.PermissionManagement.MongoDB.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo.Abp.PermissionManagement.TestBase.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo.Abp.PermissionManagement.TestBase.csproj index bdaa26deea..1e948ab334 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo.Abp.PermissionManagement.TestBase.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo.Abp.PermissionManagement.TestBase.csproj @@ -18,12 +18,12 @@ - - + + - + diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs index 44a21f3ccb..e23a0e39a5 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs @@ -2,10 +2,26 @@ { public static class SettingConsts { - public const int MaxNameLength = 128; - public const int MaxValueLength = 2048; + /// + /// Default value: 128 + /// + public static int MaxNameLength { get; set; } = 128; + + /// + /// Default value: 2048 + /// + public static int MaxValueLength { get; set; } = 2048; + public static int MaxValueLengthValue { get; set; } = MaxValueLength; - public const int MaxProviderNameLength = 64; - public const int MaxProviderKeyLength = 64; + + /// + /// Default value: 64 + /// + public static int MaxProviderNameLength { get; set; } = 64; + + /// + /// Default value: 64 + /// + public static int MaxProviderKeyLength { get; set; } = 64; } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs index d7a8b0ccb1..617d360898 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs @@ -11,23 +11,25 @@ namespace Volo.Abp.SettingManagement public class SettingManagementStore : ISettingManagementStore, ITransientDependency { protected IDistributedCache Cache { get; } + protected ISettingDefinitionManager SettingDefinitionManager { get; } protected ISettingRepository SettingRepository { get; } protected IGuidGenerator GuidGenerator { get; } public SettingManagementStore( ISettingRepository settingRepository, IGuidGenerator guidGenerator, - IDistributedCache cache) + IDistributedCache cache, + ISettingDefinitionManager settingDefinitionManager) { SettingRepository = settingRepository; GuidGenerator = guidGenerator; Cache = cache; + SettingDefinitionManager = settingDefinitionManager; } public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) { - var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); - return cacheItem.Value; + return (await GetCacheItemAsync(name, providerName, providerKey)).Value; } public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) @@ -70,16 +72,43 @@ namespace Volo.Abp.SettingManagement return cacheItem; } - var setting = await SettingRepository.FindAsync(name, providerName, providerKey); + cacheItem = new SettingCacheItem(null); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); + + return cacheItem; + } - cacheItem = new SettingCacheItem(setting?.Value); + private async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + SettingCacheItem currentCacheItem) + { + var settingDefinitions = SettingDefinitionManager.GetAll(); + var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var settingDefinition in settingDefinitions) + { + var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); + + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(settingDefinition.Name, providerName, providerKey), + new SettingCacheItem(settingValue) + ) + ); - await Cache.SetAsync( - cacheKey, - cacheItem - ); + if (settingDefinition.Name == currentName) + { + currentCacheItem.Value = settingValue; + } + } - return cacheItem; + await Cache.SetManyAsync(cacheItems); } protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json deleted file mode 100644 index 844334b642..0000000000 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:62272/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.SettingManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:62273/" - } - } -} \ No newline at end of file diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj index b37ef515df..a0197c0686 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj @@ -24,8 +24,6 @@ - - diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj index 83ab36608b..b60b661a57 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj index 2e2ab114e4..5c68ed3b08 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo.Abp.SettingManagement.MongoDB.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo.Abp.SettingManagement.TestBase.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo.Abp.SettingManagement.TestBase.csproj index d6511ec8cf..6b2d84a3f3 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo.Abp.SettingManagement.TestBase.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo.Abp.SettingManagement.TestBase.csproj @@ -18,12 +18,12 @@ - - + + - + diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo.Abp.SettingManagement.Tests.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo.Abp.SettingManagement.Tests.csproj index ea519cf662..03fc9d8a4a 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo.Abp.SettingManagement.Tests.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo.Abp.SettingManagement.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs index 19f73b0d51..7dc805ea79 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs @@ -1,12 +1,13 @@ using System.ComponentModel.DataAnnotations; using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation; namespace Volo.Abp.TenantManagement { public abstract class TenantCreateOrUpdateDtoBase : ExtensibleObject { [Required] - [StringLength(TenantConsts.MaxNameLength)] + [DynamicStringLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))] public string Name { get; set; } } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs index 4cdf8dee2a..b177e8d046 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs @@ -2,8 +2,14 @@ { public static class TenantConnectionStringConsts { - public const int MaxNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxNameLength { get; set; } = 64; - public const int MaxValueLength = 1024; + /// + /// Default value: 1024 + /// + public static int MaxValueLength { get; set; } = 1024; } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs index 328c422ab9..10de5e8773 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs @@ -2,6 +2,9 @@ { public static class TenantConsts { - public const int MaxNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxNameLength { get; set; } = 64; } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj index 28541197c4..dc168af444 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj @@ -21,7 +21,6 @@ - diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs index 5bf2aeb0b9..1f512c8807 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs @@ -5,7 +5,6 @@ using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; -using Volo.Abp.UI; namespace Volo.Abp.TenantManagement { @@ -14,7 +13,6 @@ namespace Volo.Abp.TenantManagement [DependsOn(typeof(AbpDataModule))] [DependsOn(typeof(AbpDddDomainModule))] [DependsOn(typeof(AbpAutoMapperModule))] - [DependsOn(typeof(AbpUiModule))] //TODO: It's not good to depend on the UI module. However, UserFriendlyException is inside it! public class AbpTenantManagementDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs index d52734a667..a03c96142f 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Volo.Abp.Domain.Services; -using Volo.Abp.UI; namespace Volo.Abp.TenantManagement { diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs index 9093437f98..45a36060e0 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants { @@ -53,7 +54,7 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants public bool UseSharedDatabase { get; set; } - [StringLength(TenantConnectionStringConsts.MaxValueLength)] + [DynamicStringLength(typeof(TenantConnectionStringConsts), nameof(TenantConnectionStringConsts.MaxValueLength))] public string DefaultConnectionString { get; set; } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs index fa517594ee..0c1e6e0bec 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants { @@ -34,7 +35,7 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants public class TenantInfoModel { [Required] - [StringLength(TenantConsts.MaxNameLength)] + [DynamicStringLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))] public string Name { get; set; } [Required] diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs index 74ac176b02..736d0174cf 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Validation; namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants { @@ -42,7 +43,7 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants public Guid Id { get; set; } [Required] - [StringLength(TenantConsts.MaxNameLength)] + [DynamicStringLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))] [Display(Name = "DisplayName:TenantName")] public string Name { get; set; } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json deleted file mode 100644 index dab1d2b972..0000000000 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55673/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Volo.Abp.TenantManagement.Web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55676/" - } - } -} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj index c558ea9637..71ec4cc04a 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj @@ -19,9 +19,7 @@ - - diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj index 77b220f937..1183bde9bd 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo.Abp.TenantManagement.Domain.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo.Abp.TenantManagement.Domain.Tests.csproj index 3d8b2073f6..e9d20712ea 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo.Abp.TenantManagement.Domain.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo.Abp.TenantManagement.Domain.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj index 5030a26cfb..385b09f899 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj index e7a10843ba..5306886296 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj index 765d7ef219..6035cd7cce 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj @@ -18,12 +18,12 @@ - - + + - + diff --git a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs index acfce7f37b..efbd2e02b2 100644 --- a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs +++ b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs @@ -2,14 +2,29 @@ namespace Volo.Abp.Users { public class AbpUserConsts { - public const int MaxUserNameLength = 256; + /// + /// Default value: 256 + /// + public static int MaxUserNameLength { get; set; } = 256; - public const int MaxNameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxNameLength { get; set; } = 64; - public const int MaxSurnameLength = 64; + /// + /// Default value: 64 + /// + public static int MaxSurnameLength { get; set; } = 64; - public const int MaxEmailLength = 256; + /// + /// Default value: 256 + /// + public static int MaxEmailLength { get; set; } = 256; - public const int MaxPhoneNumberLength = 16; + /// + /// Default value: 16 + /// + public static int MaxPhoneNumberLength { get; set; } = 16; } } \ No newline at end of file diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Localization/Resources/zh-Hant.json b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Localization/Resources/zh-Hant.json index 52291dead1..1fb05fc6bf 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Localization/Resources/zh-Hant.json +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Localization/Resources/zh-Hant.json @@ -1,6 +1,14 @@ { "culture": "zh-Hant", "texts": { - + "VirtualFileExplorer" : "虛擬檔案總管", + "VirtualFileType" : "虛擬檔案類型", + "Menu:VirtualFileExplorer" : "虛擬檔案瀏覽器", + "LastUpdateTime" : "最後更新時間", + "VirtualFileName" : "虛擬檔案名稱", + "FileContent" : "檔案內容", + "Size" : "檔案大小", + "BackToRoot" : "返回根目錄", + "EmptyFileInfoList" : "無任何虛擬檔案" } } diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj index 6cbf49a69c..b2f5d2a4a8 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj @@ -30,9 +30,4 @@ - - - - - diff --git a/npm/ng-packs/apps/dev-app/browserslist b/npm/ng-packs/apps/dev-app/.browserslistrc similarity index 100% rename from npm/ng-packs/apps/dev-app/browserslist rename to npm/ng-packs/apps/dev-app/.browserslistrc diff --git a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts index be5089316a..8f6973079d 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts @@ -1,36 +1,37 @@ -import { ABP } from '@abp/ng.core'; +import { ABP, DynamicLayoutComponent } from '@abp/ng.core'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', - loadChildren: () => import('./home/home.module').then(m => m.HomeModule), - data: { - routes: { - name: '::Menu:Home', - order: 1, - } as ABP.Route, - }, - }, - { - 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()), + 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()), + }, + ], }, ]; diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index acf8fcaaa8..4e69d73cd0 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -3,6 +3,7 @@ import { CoreModule } from '@abp/ng.core'; import { IdentityConfigModule } from '@abp/ng.identity/config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; +import { ThemeBasicModule } from '@abp/ng.theme.basic'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; @@ -12,7 +13,7 @@ import { NgxsModule } from '@ngxs/store'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; -import { SharedModule } from './shared/shared.module'; +import { APP_ROUTE_PROVIDER } from './route.provider'; const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @@ -32,9 +33,10 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; TenantManagementConfigModule.forRoot(), SettingManagementConfigModule.forRoot(), NgxsModule.forRoot(), - SharedModule, + ThemeBasicModule.forRoot(), ...(environment.production ? [] : LOGGERS), ], + providers: [APP_ROUTE_PROVIDER], declarations: [AppComponent], bootstrap: [AppComponent], }) diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts index 367affb583..7089990134 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/home/home-routing.module.ts @@ -1,15 +1,8 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home.component'; -import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; -const routes: Routes = [ - { - path: '', - component: ApplicationLayoutComponent, - children: [{ path: '', component: HomeComponent }], - }, -]; +const routes: Routes = [{ path: '', component: HomeComponent }]; @NgModule({ imports: [RouterModule.forChild(routes)], diff --git a/npm/ng-packs/apps/dev-app/src/app/route.provider.ts b/npm/ng-packs/apps/dev-app/src/app/route.provider.ts new file mode 100644 index 0000000000..5a14ad7ba0 --- /dev/null +++ b/npm/ng-packs/apps/dev-app/src/app/route.provider.ts @@ -0,0 +1,20 @@ +import { RoutesService, eLayoutType } from '@abp/ng.core'; +import { APP_INITIALIZER } from '@angular/core'; + +export const APP_ROUTE_PROVIDER = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/', + name: '::Menu:Home', + iconClass: 'fas fa-home', + order: 1, + layout: eLayoutType.application, + }, + ]); + }; +} diff --git a/npm/ng-packs/apps/dev-app/tsconfig.dev.json b/npm/ng-packs/apps/dev-app/tsconfig.dev.json index cfbd94952d..ac0a5c3d24 100644 --- a/npm/ng-packs/apps/dev-app/tsconfig.dev.json +++ b/npm/ng-packs/apps/dev-app/tsconfig.dev.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "../../out-tsc/app", "types": [] diff --git a/npm/ng-packs/apps/dev-app/tsconfig.spec.json b/npm/ng-packs/apps/dev-app/tsconfig.spec.json index e487d98612..f21e414d94 100644 --- a/npm/ng-packs/apps/dev-app/tsconfig.spec.json +++ b/npm/ng-packs/apps/dev-app/tsconfig.spec.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "../../out-tsc/spec", "types": ["node", "jest"] diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 37476c4fd8..6ea5faeeae 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -19,7 +19,7 @@ "ci:test": "ng test --coverage=false", "ci:build": "cd scripts && yarn build:prod", "lerna": "lerna", - "compile:ivy": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json'", + "compile:ivy": "yarn ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json' --source node_modules --async false", "postinstall": "npm run compile:ivy" }, "devDependencies": { @@ -32,35 +32,35 @@ "@abp/ng.tenant-management": "~2.9.0", "@abp/ng.theme.basic": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0", - "@abp/utils": "^2.9.0", + "@abp/utils": "^3.0.0-alpha.1", "@angular-builders/jest": "^9.0.1", - "@angular-devkit/build-angular": "~0.901.7", - "@angular-devkit/build-ng-packagr": "~0.901.7", - "@angular/animations": "~9.1.9", - "@angular/cli": "~9.1.7", - "@angular/common": "~9.1.9", - "@angular/compiler": "~9.1.9", - "@angular/compiler-cli": "~9.1.9", - "@angular/core": "~9.1.9", - "@angular/forms": "~9.1.9", - "@angular/language-service": "~9.1.9", - "@angular/localize": "~9.1.9", - "@angular/platform-browser": "~9.1.9", - "@angular/platform-browser-dynamic": "~9.1.9", - "@angular/router": "~9.1.9", + "@angular-devkit/build-angular": "~0.1000.0", + "@angular-devkit/build-ng-packagr": "~0.1000.0", + "@angular/animations": "~10.0.0", + "@angular/cli": "~10.0.0", + "@angular/common": "~10.0.0", + "@angular/compiler": "~10.0.0", + "@angular/compiler-cli": "~10.0.0", + "@angular/core": "~10.0.0", + "@angular/forms": "~10.0.0", + "@angular/language-service": "~10.0.0", + "@angular/localize": "~10.0.0", + "@angular/platform-browser": "~10.0.0", + "@angular/platform-browser-dynamic": "~10.0.0", + "@angular/router": "~10.0.0", "@fortawesome/fontawesome-free": "^5.13.0", "@ng-bootstrap/ng-bootstrap": "^6.1.0", - "@ngneat/spectator": "^4.5.0", - "@ngx-validate/core": "^0.0.8", + "@ngneat/spectator": "^5.11.0", + "@ngx-validate/core": "^0.0.9", "@ngxs/devtools-plugin": "^3.6.2", "@ngxs/logger-plugin": "^3.6.2", "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", - "@swimlane/ngx-datatable": "^16.0.0", + "@swimlane/ngx-datatable": "^17.0.0", "@types/jest": "^25.2.3", "@types/node": "^12.11.1", - "angular-oauth2-oidc": "^8.0.4", + "angular-oauth2-oidc": "^9.2.2", "bootstrap": "^4.5.0", "chart.js": "^2.9.3", "codelyzer": "^5.1.2", @@ -73,23 +73,23 @@ "just-clone": "^3.1.0", "just-compare": "^1.3.0", "lerna": "^3.19.0", - "ng-packagr": "^9.1.5", + "ng-packagr": "^10.0.0", "ngxs-reset-plugin": "^1.3.0", "ngxs-schematic": "^1.1.9", "prettier": "^1.18.2", - "protractor": "~5.4.0", + "protractor": "~7.0.0", "rxjs": "~6.5.4", "snq": "^1.0.3", "symlink-manager": "^1.5.0", "ts-node": "~7.0.0", - "ts-toolbelt": "^6.3.6", - "tsickle": "^0.37.0", - "tslint": "~5.20.0", - "typescript": "~3.8.0", + "ts-toolbelt": "^6.9.9", + "tsickle": "^0.38.1", + "tslint": "~6.1.0", + "typescript": "~3.9.5", "zone.js": "~0.10.2" }, "dependencies": { - "tslib": "^1.10.0" + "tslib": "^2.0.0" }, "config": { "commitizen": { @@ -99,4 +99,4 @@ "resolutions": { "@ngx-validate/core": "^0.0.8" } -} +} \ No newline at end of file diff --git a/npm/ng-packs/packages/account/ngcc.config.js b/npm/ng-packs/packages/account/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/account/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index 254b6031a1..9f4c7942d2 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.theme.shared": "~2.9.0" - }, + , "tslib": "^2.0.0" +}, "publishConfig": { "access": "public" } diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index fd326aae58..5566fb9750 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -8,15 +8,16 @@ }, "dependencies": { "@abp/utils": "^2.9.0", - "@angular/localize": "~9.1.9", + "@angular/localize": "~9.1.11", "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", - "angular-oauth2-oidc": "^8.0.4", + "angular-oauth2-oidc": "^9.2.2", "just-clone": "^3.1.0", "just-compare": "^1.3.0", "snq": "^1.0.3", - "ts-toolbelt": "^6.3.6" + "ts-toolbelt": "^6.9.9", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 5455187086..49a0868967 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnDestroy, Type } from '@angular/core'; +import { Component, Injector, OnDestroy, Type, Optional, SkipSelf } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngxs/store'; import { eLayoutType } from '../enums/common'; @@ -6,7 +6,7 @@ import { ABP } from '../models'; import { ReplaceableComponents } from '../models/replaceable-components'; import { RoutesService } from '../services/routes.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; -import { getRoutePath } from '../utils/route-utils'; +import { findRoute, getRoutePath } from '../utils/route-utils'; import { takeUntilDestroy } from '../utils/rxjs-utils'; import { TreeNode } from '../utils/tree-utils'; @@ -25,11 +25,13 @@ export class DynamicLayoutComponent implements OnDestroy { constructor( injector: Injector, - private route: ActivatedRoute, - private routes: RoutesService, private store: Store, + @Optional() @SkipSelf() dynamicLayoutComponent: DynamicLayoutComponent, ) { + if (dynamicLayoutComponent) return; + const route = injector.get(ActivatedRoute); const router = injector.get(Router); + const routes = injector.get(RoutesService); const layouts = { application: this.getComponent('Theme.ApplicationLayoutComponent'), account: this.getComponent('Theme.AccountLayoutComponent'), @@ -38,11 +40,12 @@ export class DynamicLayoutComponent implements OnDestroy { router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { - let expectedLayout = (this.route.snapshot.data || {}).layout; - const path = getRoutePath(router); + let expectedLayout = (route.snapshot.data || {}).layout; if (!expectedLayout) { - let node = { parent: this.routes.search({ path }) } as TreeNode; + let node = findRoute(routes, getRoutePath(router)); + node = { parent: node } as TreeNode; + while (node.parent) { node = node.parent; diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 72aeacb29d..5023876a4d 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -21,6 +21,7 @@ import { PermissionDirective } from './directives/permission.directive'; import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; import { StopPropagationDirective } from './directives/stop-propagation.directive'; import { VisibilityDirective } from './directives/visibility.directive'; +import { RoutesHandler } from './handlers/routes.handler'; import { ApiInterceptor } from './interceptors/api.interceptor'; import { LocalizationModule } from './localization.module'; import { ABP } from './models/common'; @@ -196,6 +197,12 @@ export class CoreModule { deps: [LocalizationService], useFactory: noop, }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [RoutesHandler], + useFactory: noop, + }, { provide: OAuthStorage, useFactory: storageFactory }, ], }; diff --git a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts index 2dffa1532d..f5f4d22f5e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts @@ -2,6 +2,10 @@ import { Directive, Input, Optional, ElementRef, Renderer2, AfterViewInit } from import { Subject } from 'rxjs'; import snq from 'snq'; +/** + * + * @deprecated To be deleted in v3.3 + */ @Directive({ selector: '[abpVisibility]', }) @@ -40,7 +44,8 @@ export class VisibilityDirective implements AfterViewInit { setTimeout(() => { const htmlNodes = snq( - () => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), + () => + Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), [], ); diff --git a/npm/ng-packs/packages/core/src/lib/handlers/index.ts b/npm/ng-packs/packages/core/src/lib/handlers/index.ts index e69de29bb2..b666218d93 100644 --- a/npm/ng-packs/packages/core/src/lib/handlers/index.ts +++ b/npm/ng-packs/packages/core/src/lib/handlers/index.ts @@ -0,0 +1 @@ +export * from './routes.handler'; diff --git a/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts new file mode 100644 index 0000000000..9ddc03e493 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts @@ -0,0 +1,45 @@ +import { Injectable, Optional } from '@angular/core'; +import { Router } from '@angular/router'; +import { ABP } from '../models'; +import { RoutesService } from '../services/routes.service'; + +@Injectable({ + providedIn: 'root', +}) +export class RoutesHandler { + constructor(private routes: RoutesService, @Optional() private router: Router) { + this.addRoutes(); + } + + addRoutes() { + this.router?.config?.forEach(({ path = '', data }) => { + if (!data?.routes) return; + + if (Array.isArray(data.routes)) { + this.routes.add(data.routes); + return; + } + + const routes = flatRoutes([{ path, ...data.routes }], { path: '' }); + this.routes.add(routes); + }); + } +} + +function flatRoutes(routes: RouteDef[], parent: any) { + if (!routes) return []; + + return routes.reduce((acc, route) => { + const { children, ...current } = { + ...route, + parentName: parent.name, + path: (parent.path + '/' + route.path).replace(/\/\//g, '/'), + }; + + acc.push(current, ...flatRoutes(children, current)); + + return acc; + }, []); +} + +type RouteDef = ABP.Route & { children: RouteDef[] }; diff --git a/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts b/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts index b52afe9faf..1f3bb65f4e 100644 --- a/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts +++ b/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts @@ -62,10 +62,11 @@ export namespace ApplicationConfiguration { } export interface CurrentUser { - isAuthenticated: boolean; + email: string; id: string; + isAuthenticated: boolean; + roles: string[]; tenantId: string; userName: string; - email: string; } } diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 79e3d3f52b..930265020c 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -30,19 +30,16 @@ export namespace ABP { maxResultCount?: number; } - export interface Node { + export interface Nav { name: string; parentName?: string; + requiredPolicy?: string; order?: number; invisible?: boolean; } - export interface Nav extends Node { - path: string; - requiredPolicy?: string; - } - export interface Route extends Nav { + path: string; layout?: eLayoutType; iconClass?: string; } diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts index 0c38b22acf..35f667bf20 100644 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -20,14 +20,14 @@ export class AuthService { private oAuthService: OAuthService, private store: Store, @Optional() @Inject('ACCOUNT_OPTIONS') private options: any, - ) {} - - login(username: string, password: string): Observable { - const tenant = this.store.selectSnapshot(SessionState.getTenant); - + ) { this.oAuthService.configure( this.store.selectSnapshot(ConfigState.getOne('environment')).oAuthConfig, ); + } + + login(username: string, password: string): Observable { + const tenant = this.store.selectSnapshot(SessionState.getTenant); return from(this.oAuthService.loadDiscoveryDocument()).pipe( switchMap(() => @@ -63,7 +63,7 @@ export class AuthService { ) .pipe( switchMap(() => { - this.oAuthService.logOut(); + this.oAuthService.logOut(true); return this.store.dispatch(new GetAppConfiguration()); }), ); diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 4b9f3e7899..4a3426dd51 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,8 +1,10 @@ -import { Injectable } from '@angular/core'; -import { Store } from '@ngxs/store'; +import { Injectable, OnDestroy } from '@angular/core'; +import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { BehaviorSubject, Observable } from 'rxjs'; +import { GetAppConfiguration } from '../actions/config.actions'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; +import { takeUntilDestroy } from '../utils/rxjs-utils'; import { pushValueTo } from '../utils/array-utils'; import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; @@ -75,6 +77,13 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } + find(predicate: (item: TreeNode) => boolean, tree = this.tree): TreeNode | null { + return tree.reduce( + (acc, node) => (acc ? acc : predicate(node) ? node : this.find(predicate, node.children)), + null, + ); + } + patch(identifier: string, props: Partial): T[] | false { const flatItems = this._flat$.value; const index = flatItems.findIndex(item => item[this.id] === identifier); @@ -88,6 +97,10 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } + refresh(): T[] { + return this.add([]); + } + remove(identifiers: string[]): T[] { const set = new Set(); identifiers.forEach(id => set.add(id)); @@ -98,7 +111,7 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } - search(params: Partial, tree = this.tree): TreeNode { + search(params: Partial, tree = this.tree): TreeNode | null { const searchKeys = Object.keys(params); return tree.reduce( @@ -107,38 +120,48 @@ export abstract class AbstractTreeService { ? acc : searchKeys.every(key => node[key] === params[key]) ? node - : node.children - ? this.search(params, node.children) - : acc, + : this.search(params, node.children), null, ); } } -@Injectable({ - providedIn: 'root', -}) -export class RoutesService extends AbstractTreeService { +@Injectable() +export abstract class AbstractNavTreeService extends AbstractTreeService + implements OnDestroy { readonly id = 'name'; readonly parentId = 'parentName'; - readonly hide = (item: ABP.Route) => item.invisible; - readonly sort = (a: ABP.Route, b: ABP.Route) => a.order - b.order; -} + readonly hide = (item: T) => item.invisible || !this.isGranted(item); + readonly sort = (a: T, b: T) => { + if (!a.order) return 1; + if (!b.order) return -1; -@Injectable({ - providedIn: 'root', -}) -export class SettingTabsService extends AbstractTreeService { - readonly id = 'name'; - readonly parentId = 'parentName'; - readonly hide = (setting: ABP.Tab) => setting.invisible || !this.isGranted(setting); - readonly sort = (a: ABP.Tab, b: ABP.Tab) => a.order - b.order; + return a.order - b.order; + }; - constructor(private store: Store) { + constructor(protected actions: Actions, protected store: Store) { super(); + + this.actions + .pipe(takeUntilDestroy(this), ofActionSuccessful(GetAppConfiguration)) + .subscribe(() => this.refresh()); + } + + protected isGranted({ requiredPolicy }: T): boolean { + return this.store.selectSnapshot(ConfigState.getGrantedPolicy(requiredPolicy)); } - private isGranted(setting: ABP.Tab): boolean { - return this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)); + hasInvisibleChild(identifier: string): boolean { + const node = this.find(item => item[this.id] === identifier); + return node?.children?.some(child => child.invisible); } + + /* istanbul ignore next */ + ngOnDestroy() {} } + +@Injectable({ providedIn: 'root' }) +export class RoutesService extends AbstractNavTreeService {} + +@Injectable({ providedIn: 'root' }) +export class SettingTabsService extends AbstractNavTreeService {} diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index d9b738b2aa..d9793529aa 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -93,23 +93,24 @@ export class ConfigState { static getGrantedPolicy(key: string) { const selector = createSelector([ConfigState], (state: Config.State): boolean => { if (!key) return true; - const getPolicy = k => snq(() => state.auth.grantedPolicies[k], false); + const getPolicy = (k: string) => snq(() => state.auth.grantedPolicies[k], false); const orRegexp = /\|\|/g; const andRegexp = /&&/g; + // TODO: Allow combination of ANDs & ORs if (orRegexp.test(key)) { - const keys = key.split('||').filter(k => !!k); + const keys = key.split('||').filter(Boolean); - if (keys.length !== 2) return false; + if (keys.length < 2) return false; - return getPolicy(keys[0].trim()) || getPolicy(keys[1].trim()); + return keys.some(k => getPolicy(k.trim())); } else if (andRegexp.test(key)) { - const keys = key.split('&&').filter(k => !!k); + const keys = key.split('&&').filter(Boolean); - if (keys.length !== 2) return false; + if (keys.length < 2) return false; - return getPolicy(keys[0].trim()) && getPolicy(keys[1].trim()); + return keys.every(k => getPolicy(k.trim())); } return getPolicy(key); @@ -189,7 +190,7 @@ export class ConfigState { @Action(GetAppConfiguration) addData({ patchState, dispatch }: StateContext) { - const apiName = this.store.selectSnapshot(ConfigState.getDeep('environment.application.name')); + const apiName = 'default'; const api = this.store.selectSnapshot(ConfigState.getApiUrl(apiName)); return this.http .get(`${api}/api/abp/application-configuration`) diff --git a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts index a34e3eaa4e..4b0c9aa682 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts @@ -1,9 +1,9 @@ import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; -import { ConfigStateService } from '../services/config-state.service'; -import { ConfigState } from '../states'; import { Store } from '@ngxs/store'; -import { Config } from '../models/config'; import * as ConfigActions from '../actions'; +import { Config } from '../models/config'; +import { ConfigStateService } from '../services/config-state.service'; +import { ConfigState } from '../states'; const CONFIG_STATE_DATA = { environment: { @@ -86,6 +86,7 @@ const CONFIG_STATE_DATA = { tenantId: null, userName: null, email: null, + roles: [], }, features: { values: {}, diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts index 5966ba0728..0f9aa08c1a 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts @@ -90,6 +90,7 @@ export const CONFIG_STATE_DATA = { tenantId: null, userName: null, email: null, + roles: [], }, features: { values: {}, diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index 89a6a683b9..216df7b681 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -2,7 +2,8 @@ import { HttpClient } from '@angular/common/http'; import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; -import { NgxsModule, Store } from '@ngxs/store'; +import { Actions, NgxsModule, Store } from '@ngxs/store'; +import { NEVER } from 'rxjs'; import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; import { eLayoutType } from '../enums/common'; import { ABP } from '../models'; @@ -97,11 +98,24 @@ const storeData = { }; describe('DynamicLayoutComponent', () => { + const mockActions: Actions = NEVER; + const mockStore = ({ + selectSnapshot() { + return true; + }, + } as unknown) as Store; + const createComponent = createRoutingFactory({ component: RouterOutletComponent, stubsEnabled: false, declarations: [DummyComponent, DynamicLayoutComponent], mocks: [ApplicationConfigurationService, HttpClient], + providers: [ + { + provide: RoutesService, + useFactory: () => new RoutesService(mockActions, mockStore), + }, + ], imports: [RouterModule, DummyLayoutModule, NgxsModule.forRoot([ReplaceableComponentsState])], routes: [ { path: '', component: RouterOutletComponent }, diff --git a/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts index 41a761b84d..3945a6527d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts @@ -2,13 +2,35 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; import { RouterOutletComponent } from '../components'; -import { getRoutePath } from '../utils/route-utils'; +import { RoutesService } from '../services/routes.service'; +import { findRoute, getRoutePath } from '../utils/route-utils'; // tslint:disable-next-line @Component({ template: '' }) class DummyComponent {} describe('Route Utils', () => { + describe('#findRoute', () => { + const node = { path: '/foo' }; + + test.each` + path | expected | count + ${'/foo/bar/baz'} | ${node} | ${3} + ${'/foo/bar'} | ${node} | ${2} + ${'/foo'} | ${node} | ${1} + ${'/'} | ${null} | ${1} + `( + 'should find $expected in $count turns when path is $path', + async ({ path, expected, count }) => { + const find = jest.fn(cb => (cb(node) ? node : null)); + const routes = ({ find } as any) as RoutesService; + const route = findRoute(routes, path); + expect(route).toBe(expected); + expect(find).toHaveBeenCalledTimes(count); + }, + ); + }); + describe('#getRoutePath', () => { let spectator: SpectatorRouting; const createRouting = createRoutingFactory({ diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts new file mode 100644 index 0000000000..777df39c60 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts @@ -0,0 +1,40 @@ +import { Router } from '@angular/router'; +import { RoutesHandler } from '../handlers'; +import { RoutesService } from '../services'; + +describe('Routes Handler', () => { + describe('#add', () => { + it('should add routes from router config', () => { + const config = [ + { path: 'x' }, + { path: 'y', data: {} }, + { path: '', data: { routes: { name: 'Foo' } } }, + { path: 'bar', data: { routes: { name: 'Bar' } } }, + { data: { routes: [{ path: '/baz', name: 'Baz' }] } }, + ]; + const foo = [{ path: '/', name: 'Foo' }]; + const bar = [{ path: '/bar', name: 'Bar' }]; + const baz = [{ path: '/baz', name: 'Baz' }]; + + const routes = []; + const add = jest.fn(routes.push.bind(routes)); + const mockRoutesService = ({ add } as unknown) as RoutesService; + const mockRouter = ({ config } as unknown) as Router; + + const handler = new RoutesHandler(mockRoutesService, mockRouter); + + expect(add).toHaveBeenCalledTimes(3); + expect(routes).toEqual([foo, bar, baz]); + }); + + it('should not add routes when there is no router', () => { + const routes = []; + const add = jest.fn(routes.push.bind(routes)); + const mockRoutesService = ({ add } as unknown) as RoutesService; + + const handler = new RoutesHandler(mockRoutesService, null); + + expect(add).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts index 2392a86183..c1413d73ab 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts @@ -1,17 +1,31 @@ +import { Store } from '@ngxs/store'; +import { Subject } from 'rxjs'; import { take } from 'rxjs/operators'; +import { GetAppConfiguration } from '../actions'; import { RoutesService } from '../services'; -const routes = [ - { path: '/foo', name: 'foo' }, - { path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, - { path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, - { path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, -]; - describe('Routes Service', () => { + let service: RoutesService; + const routes = [ + { path: '/foo', name: 'foo' }, + { path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, + { path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, + { path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, + ]; + + const mockActions = new Subject(); + const mockStore = ({ + selectSnapshot() { + return true; + }, + } as unknown) as Store; + + beforeEach(() => { + service = new RoutesService(mockActions, mockStore); + }); + describe('#add', () => { it('should add given routes as flat$, tree$, and visible$', async () => { - const service = new RoutesService(); service.add(routes); const flat = await service.flat$.pipe(take(1)).toPromise(); @@ -19,10 +33,10 @@ describe('Routes Service', () => { const visible = await service.visible$.pipe(take(1)).toPromise(); expect(flat.length).toBe(4); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('baz'); - expect(flat[2].name).toBe('x'); - expect(flat[3].name).toBe('bar'); + expect(flat[3].name).toBe('foo'); + expect(flat[0].name).toBe('baz'); + expect(flat[1].name).toBe('x'); + expect(flat[2].name).toBe('bar'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); @@ -38,9 +52,34 @@ describe('Routes Service', () => { }); }); + describe('#find', () => { + it('should return node found based on query', () => { + service.add(routes); + const result = service.find(route => route.invisible); + expect(result.name).toBe('bar'); + expect(result.children.length).toBe(1); + expect(result.children[0].name).toBe('baz'); + }); + + it('should return null when query is not found', () => { + service.add(routes); + const result = service.find(route => route.requiredPolicy === 'X'); + expect(result).toBe(null); + }); + }); + + describe('#hasInvisibleChild', () => { + it('should return if node has invisible child', () => { + service.add(routes); + + expect(service.hasInvisibleChild('foo')).toBe(true); + expect(service.hasInvisibleChild('bar')).toBe(false); + expect(service.hasInvisibleChild('baz')).toBe(false); + }); + }); + describe('#remove', () => { it('should remove routes based on given routeNames', () => { - const service = new RoutesService(); service.add(routes); service.remove(['bar']); @@ -49,8 +88,8 @@ describe('Routes Service', () => { const visible = service.visible; expect(flat.length).toBe(2); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('x'); + expect(flat[1].name).toBe('foo'); + expect(flat[0].name).toBe('x'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); @@ -66,19 +105,19 @@ describe('Routes Service', () => { describe('#patch', () => { it('should patch propeties of routes based on given routeNames', () => { - const service = new RoutesService(); + service['isGranted'] = jest.fn(route => route.requiredPolicy !== 'X'); service.add(routes); - service.patch('x', { invisible: true }); + service.patch('x', { requiredPolicy: 'X' }); const flat = service.flat; const tree = service.tree; const visible = service.visible; expect(flat.length).toBe(4); - expect(flat[0].name).toBe('foo'); - expect(flat[1].name).toBe('baz'); - expect(flat[2].name).toBe('x'); - expect(flat[3].name).toBe('bar'); + expect(flat[3].name).toBe('foo'); + expect(flat[0].name).toBe('baz'); + expect(flat[1].name).toBe('x'); + expect(flat[2].name).toBe('bar'); expect(tree.length).toBe(1); expect(tree[0].name).toBe('foo'); @@ -93,21 +132,40 @@ describe('Routes Service', () => { }); it('should return false when route name is not found', () => { - const service = new RoutesService(); service.add(routes); const result = service.patch('A man has no name.', { invisible: true }); expect(result).toBe(false); }); }); + describe('#refresh', () => { + it('should call add once with empty array', () => { + const add = jest.spyOn(service, 'add'); + service.refresh(); + expect(add).toHaveBeenCalledTimes(1); + expect(add).toHaveBeenCalledWith([]); + }); + + it('should be called upon successful GetAppConfiguration action', () => { + const refresh = jest.spyOn(service, 'refresh'); + mockActions.next({ action: new GetAppConfiguration(), status: 'SUCCESSFUL' }); + expect(refresh).toHaveBeenCalledTimes(1); + }); + }); + describe('#search', () => { - it('should return node found when route name is not found', () => { - const service = new RoutesService(); + it('should return node found based on query', () => { service.add(routes); const result = service.search({ invisible: true }); expect(result.name).toBe('bar'); expect(result.children.length).toBe(1); expect(result.children[0].name).toBe('baz'); }); + + it('should return null when query is not found', () => { + service.add(routes); + const result = service.search({ requiredPolicy: 'X' }); + expect(result).toBe(null); + }); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts index 7ef062986a..57ffdd5285 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts @@ -1,4 +1,21 @@ import { PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router'; +import { ABP } from '../models/common'; +import { RoutesService } from '../services/routes.service'; +import { TreeNode } from './tree-utils'; + +export function findRoute(routes: RoutesService, path: string): TreeNode { + const node = routes.find(route => route.path === path); + + return node || path === '/' + ? node + : findRoute( + routes, + path + .split('/') + .slice(0, -1) + .join('/'), + ); +} export function getRoutePath(router: Router) { const emptyGroup = { segments: [] } as UrlSegmentGroup; diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index fd1816df45..e2550bf15a 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts index 1020e60d4e..4aabf7bf80 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts @@ -15,7 +15,7 @@ export class FeatureManagementService { getFeatures(params: FeatureManagement.Provider): Observable { const request: Rest.Request = { method: 'GET', - url: '/api/abp/features', + url: '/api/feature-management/features', params, }; return this.rest.request(request, { @@ -30,7 +30,7 @@ export class FeatureManagementService { }: FeatureManagement.Provider & FeatureManagement.Features): Observable { const request: Rest.Request = { method: 'PUT', - url: '/api/abp/features', + url: '/api/feature-management/features', body: { features }, params: { providerKey, providerName }, }; diff --git a/npm/ng-packs/packages/identity/config/src/enums/index.ts b/npm/ng-packs/packages/identity/config/src/enums/index.ts index 3bda94b078..4a7a6a0e23 100644 --- a/npm/ng-packs/packages/identity/config/src/enums/index.ts +++ b/npm/ng-packs/packages/identity/config/src/enums/index.ts @@ -1 +1,2 @@ +export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts b/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts new file mode 100644 index 0000000000..f001c03964 --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts @@ -0,0 +1,5 @@ +export const enum eIdentityPolicyNames { + IdentityManagement = 'AbpIdentity.Roles || AbpIdentity.Users', + Roles = 'AbpIdentity.Roles', + Users = 'AbpIdentity.Users', +} diff --git a/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts index 3f88c68464..1ab2cbb3ee 100644 --- a/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts @@ -1,6 +1,7 @@ import { eLayoutType, RoutesService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; +import { eIdentityPolicyNames } from '../enums/policy-names'; import { eIdentityRouteNames } from '../enums/route-names'; export const IDENTITY_ROUTE_PROVIDERS = [ @@ -14,6 +15,7 @@ export function configureRoutes(routes: RoutesService) { path: '/identity', name: eIdentityRouteNames.IdentityManagement, parentName: eThemeSharedRouteNames.Administration, + requiredPolicy: eIdentityPolicyNames.IdentityManagement, iconClass: 'fa fa-id-card-o', layout: eLayoutType.application, order: 1, @@ -22,14 +24,14 @@ export function configureRoutes(routes: RoutesService) { path: '/identity/roles', name: eIdentityRouteNames.Roles, parentName: eIdentityRouteNames.IdentityManagement, - requiredPolicy: 'AbpIdentity.Roles', + requiredPolicy: eIdentityPolicyNames.Roles, order: 1, }, { path: '/identity/users', name: eIdentityRouteNames.Users, parentName: eIdentityRouteNames.IdentityManagement, - requiredPolicy: 'AbpIdentity.Users', + requiredPolicy: eIdentityPolicyNames.Users, order: 2, }, ]); diff --git a/npm/ng-packs/packages/identity/ngcc.config.js b/npm/ng-packs/packages/identity/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/identity/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 38ec9cd9d9..553b5518e9 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.permission-management": "~2.9.0", - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html index 648b1024d0..bf49550f12 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html @@ -102,109 +102,110 @@ >
- - - -
-
- * - -
+ + +
diff --git a/npm/ng-packs/packages/identity/src/lib/identity.module.ts b/npm/ng-packs/packages/identity/src/lib/identity.module.ts index 9aeadac6a2..036c7e3607 100644 --- a/npm/ng-packs/packages/identity/src/lib/identity.module.ts +++ b/npm/ng-packs/packages/identity/src/lib/identity.module.ts @@ -2,7 +2,7 @@ import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; import { PermissionManagementModule } from '@abp/ng.permission-management'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; -import { NgbDropdownModule, NgbTabsetModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdownModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxsModule } from '@ngxs/store'; import { RolesComponent } from './components/roles/roles.component'; @@ -17,7 +17,7 @@ import { IdentityState } from './states/identity.state'; NgxsModule.forFeature([IdentityState]), CoreModule, IdentityRoutingModule, - NgbTabsetModule, + NgbNavModule, ThemeSharedModule, NgbDropdownModule, PermissionManagementModule, diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index a09d4c7f8e..1bc7859bcf 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts index fa6433e952..4825036f81 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts @@ -16,7 +16,7 @@ export class PermissionManagementService { ): Observable { const request: Rest.Request = { method: 'GET', - url: '/api/abp/permissions', + url: '/api/permission-management/permissions', params, }; @@ -33,7 +33,7 @@ export class PermissionManagementService { }: PermissionManagement.GrantedProvider & PermissionManagement.UpdateRequest): Observable { const request: Rest.Request = { method: 'PUT', - url: '/api/abp/permissions', + url: '/api/permission-management/permissions', body: { permissions }, params: { providerKey, providerName }, }; diff --git a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts index 3ed75d33b0..28c0fb7388 100644 --- a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts @@ -21,9 +21,8 @@ export function configureRoutes(routes: RoutesService) { name: eSettingManagementRouteNames.Settings, path: '/setting-management', parentName: eThemeSharedRouteNames.Administration, - requiredPolicy: 'AbpAccount.SettingManagement', layout: eLayoutType.application, - order: 6, + order: 100, iconClass: 'fa fa-cog', }, ]); @@ -37,6 +36,6 @@ export function hideRoutes(routes: RoutesService, tabs: SettingTabsService) { debounceTime(0), map(nodes => !nodes.length), ) - .subscribe(invisible => routes.patch('AbpSettingManagement::Settings', { invisible })); + .subscribe(invisible => routes.patch(eSettingManagementRouteNames.Settings, { invisible })); }; } diff --git a/npm/ng-packs/packages/setting-management/ngcc.config.js b/npm/ng-packs/packages/setting-management/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 969510305e..a6fb57ce18 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -7,7 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts index 3bda94b078..4a7a6a0e23 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts @@ -1 +1,2 @@ +export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts new file mode 100644 index 0000000000..4b78f4d397 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts @@ -0,0 +1,4 @@ +export const enum eTenantManagementPolicyNames { + TenantManagement = 'AbpTenantManagement.Tenants', + Tenants = 'AbpTenantManagement.Tenants', +} diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts index 0be264745d..b191b46ce3 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts @@ -1,6 +1,7 @@ import { eLayoutType, RoutesService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; +import { eTenantManagementPolicyNames } from '../enums/policy-names'; import { eTenantManagementRouteNames } from '../enums/route-names'; export const TENANT_MANAGEMENT_ROUTE_PROVIDERS = [ @@ -14,6 +15,7 @@ export function configureRoutes(routes: RoutesService) { path: '/tenant-management', name: eTenantManagementRouteNames.TenantManagement, parentName: eThemeSharedRouteNames.Administration, + requiredPolicy: eTenantManagementPolicyNames.TenantManagement, layout: eLayoutType.application, iconClass: 'fa fa-users', order: 2, @@ -22,7 +24,7 @@ export function configureRoutes(routes: RoutesService) { path: '/tenant-management/tenants', name: eTenantManagementRouteNames.Tenants, parentName: eTenantManagementRouteNames.TenantManagement, - requiredPolicy: 'AbpTenantManagement.Tenants', + requiredPolicy: eTenantManagementPolicyNames.Tenants, order: 1, }, ]); diff --git a/npm/ng-packs/packages/tenant-management/ngcc.config.js b/npm/ng-packs/packages/tenant-management/ngcc.config.js new file mode 100644 index 0000000000..93f34d5655 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/ngcc.config.js @@ -0,0 +1,7 @@ +module.exports = { + entryPoints: { + '.': {}, + './config': {}, + './dist': { ignore: true }, + }, +}; diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 0921e0dd9e..e8fd10094e 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.feature-management": "~2.9.0", - "@abp/ng.theme.shared": "~2.9.0" + "@abp/ng.theme.shared": "~2.9.0", + "tslib": "^2.0.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index c7e3275e91..9307b1e21e 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@abp/ng.theme.shared": "~2.9.0" - }, + , "tslib": "^2.0.0" +}, "publishConfig": { "access": "public" } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts deleted file mode 100644 index 2e29080de8..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './layout.actions'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts deleted file mode 100644 index 3786ea82b7..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Layout } from '../models/layout'; - -export class AddNavigationElement { - static readonly type = '[Layout] Add Navigation Element'; - constructor(public payload: Layout.NavigationElement | Layout.NavigationElement[]) {} -} - -export class RemoveNavigationElementByName { - static readonly type = '[Layout] Remove Navigation ElementByName'; - constructor(public name: string) {} -} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index c4acbec880..eb68dc7df0 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -34,21 +34,15 @@
-
+ +
diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts index 06b6d5339c..6c4de6cf21 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts @@ -2,6 +2,8 @@ export * from './account-layout/account-layout.component'; export * from './application-layout/application-layout.component'; export * from './empty-layout/empty-layout.component'; export * from './logo/logo.component'; +export * from './nav-items/current-user.component'; +export * from './nav-items/languages.component'; export * from './nav-items/nav-items.component'; export * from './routes/routes.component'; export * from './validation-error/validation-error.component'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts new file mode 100644 index 0000000000..70238d3538 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts @@ -0,0 +1,67 @@ +import { ApplicationConfiguration, AuthService, ConfigState } from '@abp/ng.core'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Select } from '@ngxs/store'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'abp-current-user', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + {{ + 'AbpAccount::Login' | abpLocalization + }} + + + `, +}) +export class CurrentUserComponent implements OnInit { + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + constructor(private authService: AuthService, private router: Router) {} + + ngOnInit() {} + + logout() { + this.authService.logout().subscribe(() => { + this.router.navigate(['/'], { state: { redirectUrl: this.router.url } }); + }); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts new file mode 100644 index 0000000000..115e9e563e --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts @@ -0,0 +1,88 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Store, Select } from '@ngxs/store'; +import { SetLanguage, ConfigState, ApplicationConfiguration, SessionState } from '@abp/ng.core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import snq from 'snq'; + +@Component({ + selector: 'abp-languages', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + `, +}) +export class LanguagesComponent implements OnInit { + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + get defaultLanguage$(): Observable { + return this.languages$.pipe( + map( + languages => + snq( + () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, + ), + '', + ), + ); + } + + get dropdownLanguages$(): Observable { + return this.languages$.pipe( + map( + languages => + snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), + [], + ), + ); + } + + get selectedLangCulture(): string { + return this.store.selectSnapshot(SessionState.getLanguage); + } + + constructor(private store: Store) {} + + ngOnInit() {} + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html index cb86773db5..955ccb8878 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html @@ -1,81 +1,16 @@ - - - - + - + diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts index 6819807cd7..7a5ee9b37a 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts @@ -1,125 +1,12 @@ -import { - ABP, - ApplicationConfiguration, - AuthService, - ConfigState, - SessionState, - SetLanguage, - takeUntilDestroy, -} from '@abp/ng.core'; -import { - AfterViewInit, - Component, - Input, - OnDestroy, - TemplateRef, - TrackByFunction, - ViewChild, -} from '@angular/core'; -import { Navigate, RouterState } from '@ngxs/router-plugin'; -import { Select, Store } from '@ngxs/store'; -import compare from 'just-compare'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import snq from 'snq'; -import { AddNavigationElement } from '../../actions/layout.actions'; -import { eNavigationElementNames } from '../../enums/navigation-element-names'; -import { Layout } from '../../models/layout'; -import { LayoutState } from '../../states/layout.state'; +import { NavItem, NavItemsService } from '@abp/ng.theme.shared'; +import { Component, Input, TrackByFunction } from '@angular/core'; @Component({ selector: 'abp-nav-items', templateUrl: 'nav-items.component.html', }) -export class NavItemsComponent implements AfterViewInit, OnDestroy { - @Select(LayoutState.getNavigationElements) - navElements$: Observable; +export class NavItemsComponent { + trackByFn: TrackByFunction = (_, element) => element.id; - @Select(ConfigState.getOne('currentUser')) - currentUser$: Observable; - - @Select(ConfigState.getDeep('localization.languages')) - languages$: Observable; - - @ViewChild('currentUser', { static: false, read: TemplateRef }) - currentUserRef: TemplateRef; - - @ViewChild('language', { static: false, read: TemplateRef }) - languageRef: TemplateRef; - - @Input() - smallScreen: boolean; - - rightPartElements: TemplateRef[] = []; - - trackByFn: TrackByFunction = (_, element) => element.name; - - get defaultLanguage$(): Observable { - return this.languages$.pipe( - map( - languages => - snq( - () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, - ), - '', - ), - ); - } - - get dropdownLanguages$(): Observable { - return this.languages$.pipe( - map( - languages => - snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), - [], - ), - ); - } - - get selectedLangCulture(): string { - return this.store.selectSnapshot(SessionState.getLanguage); - } - - constructor(private store: Store, private authService: AuthService) {} - - ngAfterViewInit() { - const navigations = this.store - .selectSnapshot(LayoutState.getNavigationElements) - .map(({ name }) => name); - - if (navigations.indexOf(eNavigationElementNames.Language) < 0) { - this.store.dispatch( - new AddNavigationElement([ - { element: this.languageRef, order: 4, name: eNavigationElementNames.Language }, - { element: this.currentUserRef, order: 5, name: eNavigationElementNames.User }, - ]), - ); - } - - this.navElements$ - .pipe( - map(elements => elements.map(({ element }) => element)), - filter(elements => !compare(elements, this.rightPartElements)), - takeUntilDestroy(this), - ) - .subscribe(elements => { - setTimeout(() => (this.rightPartElements = elements), 0); - }); - } - - ngOnDestroy() {} - - onChangeLang(cultureName: string) { - this.store.dispatch(new SetLanguage(cultureName)); - } - - logout() { - this.authService.logout().subscribe(() => { - this.store.dispatch( - new Navigate(['/'], null, { - state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, - }), - ); - }); - } + constructor(public readonly navItems: NavItemsService) {} } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html index 4d22ddc8d2..42bdd7376a 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -1,7 +1,7 @@