Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into dev

pull/4543/head
Akın Sabri Çam 6 years ago
parent
commit
7cb10a8840
  1. 231
      docs/en/Tutorials/Part-1.md
  2. 576
      docs/en/Tutorials/Part-2.md
  3. BIN
      docs/en/Tutorials/images/bookstore-actions-buttons.png
  4. BIN
      docs/en/Tutorials/images/bookstore-angular-file-tree.png
  5. BIN
      docs/en/Tutorials/images/bookstore-book-list.png
  6. BIN
      docs/en/Tutorials/images/bookstore-confirmation-popup.png
  7. BIN
      docs/en/Tutorials/images/bookstore-empty-new-book-modal.png
  8. 114
      docs/en/UI/Angular/List-Service.md
  9. 216
      docs/en/UI/Angular/Modifying-the-Menu.md
  10. 6
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs
  11. 7
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorkerAdapter.cs
  12. 88
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerAdapter.cs
  13. 18
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs
  14. 26
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs
  15. 16
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateAppService.cs
  16. 21
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICreateUpdateAppService.cs
  17. 13
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs
  18. 9
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IDeleteAppService.cs
  19. 25
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IReadOnlyAppService.cs
  20. 16
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IUpdateAppService.cs
  21. 161
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs
  22. 184
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs
  23. 2
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs
  24. 66
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs
  25. 1
      framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj
  26. 2
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/AbpVirtualFileSystemOptions.cs
  27. 29
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/AbpEmbeddedFileProvider.cs
  28. 22
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedVirtualFileSetInfo.cs
  29. 10
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/IVirtualFileSet.cs
  30. 19
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Physical/PhysicalVirtualFileSetInfo.cs
  31. 2
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFilePathHelper.cs
  32. 52
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileProvider.cs
  33. 15
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetInfo.cs
  34. 9
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs
  35. 82
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs
  36. 2
      framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj
  37. 1
      framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/MyResources/js/my{test}.2.9.min.js
  38. 25
      framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo/Abp/VirtualFileSystem/VirtualFileProvider_Tests.cs
  39. 15
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogActionConsts.cs
  40. 60
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs
  41. 10
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityChangeConsts.cs
  42. 20
      modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/EntityPropertyChangeConsts.cs
  43. 10
      modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain.Shared/Volo/Abp/BackgroundJobs/BackgroundJobRecordConsts.cs
  44. 5
      modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs
  45. 7
      modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Create.cshtml.cs
  46. 7
      modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/Edit.cshtml.cs
  47. 5
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/BloggingWebConsts.cs
  48. 9
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs
  49. 15
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs
  50. 5
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs
  51. 30
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs
  52. 10
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs
  53. 9
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml.cs
  54. 9
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml.cs
  55. 11
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/GetAllInput.cs
  56. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullAllDocumentInput.cs
  57. 7
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/PullDocumentInput.cs
  58. 15
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Create.cshtml.cs
  59. 13
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Edit.cshtml.cs
  60. 7
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Pull.cshtml.cs
  61. 6
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDefaultDocumentInput.cs
  62. 7
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentInput.cs
  63. 7
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentResourceInput.cs
  64. 5
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetNavigationDocumentInput.cs
  65. 5
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetParametersDocumentInput.cs
  66. 53
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentConsts.cs
  67. 5
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Language/LanguageConsts.cs
  68. 41
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs
  69. 20
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs
  70. 19
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs
  71. 11
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs
  72. 6
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs
  73. 14
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs
  74. 11
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs
  75. 6
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs
  76. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientIdPRestrictionConsts.cs
  77. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs
  78. 11
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPropertyConsts.cs
  79. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs
  80. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientScopeConsts.cs
  81. 35
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs
  82. 17
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs
  83. 18
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs
  84. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/UserClaimConsts.cs
  85. 15
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs
  86. 24
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs
  87. 3
      modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs
  88. 10
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs
  89. 5
      modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs
  90. 3
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs
  91. 3
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs
  92. 3
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs
  93. 15
      modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs
  94. 4
      npm/ng-packs/packages/theme-shared/src/lib/components/table/table.component.ts
  95. 7
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj
  96. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs
  97. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs
  98. 2
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json
  99. 5
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj
  100. 2
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs

231
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,62 @@ 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 <==
//...added books path with the below to the routes array
const routes: Routes = [
// ...
// added a new route 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
},
loadChildren: () => import('./book/book.module').then(m => m.BookModule)
}]
```
* 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([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
// 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.
<!-- TODO: Add RoutesService doc link here -->
#### Book list component
@ -865,35 +901,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 +917,40 @@ 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<BookDto>;
}
@State<BookStateModel>({
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<BookStateModel>) {
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<BookDto[]>;
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
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 +960,31 @@ Open the `book-list.component.html` file in `app\book\book-list` folder and repl
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{%{{{ "::Menu:Books" | abpLocalization }}}%}
{%{{{ '::Menu:Books' | abpLocalization }}}%}
</h5>
</div>
<div class="text-right col col-md-6"></div>
</div>
</div>
<div class="card-body">
<abp-table
[value]="books$ | async"
[abpLoading]="loading"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[rows]="10"
[scrollable]="true"
>
</abp-table>
<ng-template #tableHeader>
<tr>
<th>{%{{{ "::Name" | abpLocalization }}}%}</th>
<th>{%{{{ "::Type" | abpLocalization }}}%}</th>
<th>{%{{{ "::PublishDate" | abpLocalization }}}%}</th>
<th>{%{{{ "::Price" | abpLocalization }}}%}</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td>{%{{{ data.name }}}%}</td>
<td>{%{{{ booksType[data.type] }}}%}</td>
<td>{%{{{ data.publishDate | date }}}%}</td>
<td>{%{{{ data.price }}}%}</td>
</tr>
</ng-template>
<ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
<ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
<ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ booksType[row.type] }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.publishDate | date }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.price | currency }}}%}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
```
@ -1074,4 +1005,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.

576
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<BookDto>;
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<BookDto>;
}
constructor(public readonly list: ListService, private bookService: BookService) {}
@State<BookStateModel>({
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<BookStateModel>) {
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<BookStateModel>, 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
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{%{{{ '::Menu:Books' | abpLocalization }}}%}
</h5>
<h5 class="card-title">{%{{{ '::Menu:Books' | abpLocalization }}}%}</h5>
</div>
<!--Added new book button -->
<!--Added new book button -->
<div class="text-right col col-md-6">
<div class="text-lg-right pt-2">
<button
id="create"
class="btn btn-primary"
type="button"
(click)="createBook()"
>
<button id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i>
<span>{%{{{ "::NewBook" | abpLocalization }}}%}</span>
</button>
@ -555,47 +520,40 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
</div>
</div>
<div class="card-body">
<abp-table
[value]="books$ | async"
[abpLoading]="loading"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[rows]="10"
[scrollable]="true"
>
</abp-table>
<ng-template #tableHeader>
<tr>
<th>{%{{{ "::Name" | abpLocalization }}}%}</th>
<th>{%{{{ "::Type" | abpLocalization }}}%}</th>
<th>{%{{{ "::PublishDate" | abpLocalization }}}%}</th>
<th>{%{{{ "::Price" | abpLocalization }}}%}</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td>{%{{{ data.name }}}%}</td>
<td>{%{{{ booksType[data.type] }}}%}</td>
<td>{%{{{ data.publishDate | date }}}%}</td>
<td>{%{{{ data.price }}}%}</td>
</tr>
</ng-template>
<ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
<ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
<ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ booksType[row.type] }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.publishDate | date }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.price | currency }}}%}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<!--added modal-->
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3>{%{{{ '::NewBook' | abpLocalization }}}%}</h3>
</ng-template>
<ng-template #abpHeader>
<h3>{%{{{ '::NewBook' | abpLocalization }}}%}</h3>
</ng-template>
<ng-template #abpBody> </ng-template>
<ng-template #abpBody> </ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button>
</ng-template>
</abp-modal>
```
@ -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<BookDto[]>;
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<BookDto[]>;
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
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<BookDto[]>;
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
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<BookDto[]>;
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
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 `<ng-template #abpFooter>` element and replace this element with the following to create a new book.
```html
<ng-template #abpFooter>
@ -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<BookStateModel>, 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<BookDto[]>;
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
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
<div class="card-body">
<abp-table
[value]="books$ | async"
[abpLoading]="loading"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[rows]="10"
[scrollable]="true"
>
</abp-table>
<ng-template #tableHeader>
<tr>
<th>{%{{{ "::Actions" | abpLocalization }}}%}</th>
<th>{%{{{ "::Name" | abpLocalization }}}%}</th>
<th>{%{{{ "::Type" | abpLocalization }}}%}</th>
<th>{%{{{ "::PublishDate" | abpLocalization }}}%}</th>
<th>{%{{{ "::Price" | abpLocalization }}}%}</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td>
<ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
<!-- added actions column -->
<ngx-datatable-column
[name]="'::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
@ -1168,25 +1006,37 @@ Open the `book-list.component.html` in `app\book\book-list` folder and replace
aria-haspopup="true"
ngbDropdownToggle
>
<i class="fa fa-cog mr-1"></i>{%{{{ "::Actions" | abpLocalization }}}%}
<i class="fa fa-cog mr-1"></i>{%{{{ '::Actions' | abpLocalization }}}%}
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="editBook(data.id)">
{%{{{ "::Edit" | abpLocalization }}}%}
<button ngbDropdownItem (click)="editBook(row.id)">
{%{{{ '::Edit' | abpLocalization }}}%}
</button>
</div>
</div>
</td>
<td>{%{{{ data.name }}}%}</td>
<td>{%{{{ booksType[data.type] }}}%}</td>
<td>{%{{{ data.publishDate | date }}}%}</td>
<td>{%{{{ data.price }}}%}</td>
</tr>
</ng-template>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
<ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ booksType[row.type] }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.publishDate | date }}}%}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
<ng-template let-row="row" ngx-datatable-cell-template>
{%{{{ row.price | currency }}}%}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
```
- 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 `<ng
```html
<ng-template #abpHeader>
<h3>{%{{{ (selectedBook.id ? 'AbpIdentity::Edit' : '::NewBook' ) | abpLocalization }}}%}</h3>
<h3>{%{{{ (selectedBook.id ? '::Edit' : '::NewBook' ) | abpLocalization }}}%}</h3>
</ng-template>
```
@ -1206,81 +1056,9 @@ Open `book-list.component.html` in `app\book\book-list` folder and find the `<ng
### Deleting a book
#### DeleteBook action
Open `book.actions.ts` in `app\book\state` folder and add an action named `DeleteBook`.
```js
export class DeleteBook {
static readonly type = '[Book] Delete';
constructor(public id: string) {}
}
```
Open the `book.state.ts` in `app\book\state` folder and replace the content as below:
```js
import { PagedResultDto } from '@abp/ng.core';
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { GetBooks, CreateUpdateBook, DeleteBook } from './book.actions'; // <== added DeleteBook==>
import { BookService } from '../services';
import { tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { BookDto } from '../models';
export class BookStateModel {
public book: PagedResultDto<BookDto>;
}
@State<BookStateModel>({
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<BookStateModel>) {
return this.bookService.getListByInput().pipe(
tap((booksResponse) => {
ctx.patchState({
book: booksResponse,
});
})
);
}
@Action(CreateUpdateBook)
save(ctx: StateContext<BookStateModel>, 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<BookStateModel>, 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
<div ngbDropdownMenu>
<!-- added Delete button -->
<button ngbDropdownItem (click)="delete(data.id)">
<button ngbDropdownItem (click)="delete(row.id)">
{%{{{ 'AbpAccount::Delete' | abpLocalization }}}%}
</button>
</div>

BIN
docs/en/Tutorials/images/bookstore-actions-buttons.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/Tutorials/images/bookstore-angular-file-tree.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 49 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 55 KiB

BIN
docs/en/Tutorials/images/bookstore-confirmation-popup.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Tutorials/images/bookstore-empty-new-book-modal.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 56 KiB

114
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
<abp-table
[value]="book.items"
[(page)]="list.page"
[rows]="list.maxResultCount"
[totalRecords]="book.totalCount"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[abpLoading]="list.isLoading$ | async"
<ngx-datatable
[rows]="items"
[count]="count"
[list]="list"
default
>
</abp-table>
<ng-template #tableHeader>
<tr>
<th (click)="nameSort.sort('name')">
{%{{{ '::Name' | abpLocalization }}}%}
<abp-sort-order-icon
#nameSort
sortKey="name"
[(selectedSortKey)]="list.sortKey"
[(order)]="list.sortOrder"
></abp-sort-order-icon>
</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td>{%{{{ data.name }}}%}</td>
</tr>
</ng-template>
<!-- column templates here -->
</ngx-datatable>
```
## 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
<!-- simplified representation of the template -->
<abp-table
[value]="(book$ | async)?.items || []"
[totalRecords]="(book$ | async)?.totalCount"
<ngx-datatable
[rows]="(book$ | async)?.items || []"
[count]="(book$ | async)?.totalCount || 0"
[list]="list"
default
>
</abp-table>
<!-- column templates here -->
</ngx-datatable>
<!-- DO NOT WORRY, ONLY ONE REQUEST WILL BE MADE -->
```
@ -113,7 +95,7 @@ You may use observables in combination with [AsyncPipe](https://angular.io/guide
...or...
```ts
```js
@Select(BookState.getBooks)
books$: Observable<BookDto[]>;
@ -128,32 +110,41 @@ You may use observables in combination with [AsyncPipe](https://angular.io/guide
```html
<!-- simplified representation of the template -->
<abp-table
[value]="books$ | async"
[totalRecords]="bookCount$ | async"
<ngx-datatable
[rows]="(books$ | async) || []"
[count]="(bookCount$ | async) || 0"
[list]="list"
default
>
</abp-table>
<!-- column templates here -->
</ngx-datatable>
```
> 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)
<input type="text" name="search" [(ngModel)]="list.filter">
```
## 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
<!-- other bindings are hidden in favor of brevity -->
<abp-table
[(page)]="list.page"
></abp-table>
```
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
<!-- other bindings are hidden in favor of brevity -->
<abp-table
[page]="list.page + 1"
(pageChange)="list.page = $event - 1"
></abp-table>
```
**Important Note:** The `abp-table` is not removed, but is deprecated and will be removed in the future. Please consider switching to ngx-datatable.

216
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<ABP.Route, 'children'>;
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<ABP.Route, 'children'>;
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<ABP.Route> = {
const newHomeRouteConfig: Partial<ABP.Route> = {
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)

6
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<IOptions<AbpBackgroundWorkerOptions>>().Value;

7
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);
}
}

88
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<TWorker> : 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<QuartzPeriodicBackgroundWorkerAdapter<TWorker>>()
.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;
}
}
}
}
}

18
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
}
}
}
}
}

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

@ -55,7 +55,7 @@ namespace Volo.Abp.Cli.Commands
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);
@ -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;
}

16
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<TEntityDto>
: ICreateAppService<TEntityDto, TEntityDto>
{
}
public interface ICreateAppService<TGetOutputDto, in TCreateInput>
: IApplicationService
{
Task<TGetOutputDto> CreateAsync(TCreateInput input);
}
}

21
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<TEntityDto, in TKey>
: ICreateUpdateAppService<TEntityDto, TKey, TEntityDto, TEntityDto>
{
}
public interface ICreateUpdateAppService<TEntityDto, in TKey, in TCreateUpdateInput>
: ICreateUpdateAppService<TEntityDto, TKey, TCreateUpdateInput, TCreateUpdateInput>
{
}
public interface ICreateUpdateAppService<TGetOutputDto, in TKey, in TCreateUpdateInput, in TUpdateInput>
: ICreateAppService<TGetOutputDto, TCreateUpdateInput>,
IUpdateAppService<TGetOutputDto, TKey, TUpdateInput>
{
}
}

13
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<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: IApplicationService
: IReadOnlyAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>,
ICreateUpdateAppService<TGetOutputDto, TKey, TCreateInput, TUpdateInput>,
IDeleteAppService<TKey>
{
Task<TGetOutputDto> GetAsync(TKey id);
Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input);
Task<TGetOutputDto> CreateAsync(TCreateInput input);
Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input);
Task DeleteAsync(TKey id);
}
}

9
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<in TKey> : IApplicationService
{
Task DeleteAsync(TKey id);
}
}

25
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<TEntityDto, in TKey>
: IReadOnlyAppService<TEntityDto, TEntityDto, TKey, PagedAndSortedResultRequestDto>
{
}
public interface IReadOnlyAppService<TEntityDto, in TKey, in TGetListInput>
: IReadOnlyAppService<TEntityDto, TEntityDto, TKey, TGetListInput>
{
}
public interface IReadOnlyAppService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput>
: IApplicationService
{
Task<TGetOutputDto> GetAsync(TKey id);
Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input);
}
}

16
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<TEntityDto, in TKey>
: IUpdateAppService<TEntityDto, TKey, TEntityDto>
{
}
public interface IUpdateAppService<TGetOutputDto, in TKey, in TUpdateInput>
: IApplicationService
{
Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input);
}
}

161
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<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: ApplicationService,
: AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>,
ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity
{
protected IRepository<TEntity> 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<TEntity> repository)
: base(repository)
{
Repository = repository;
}
public virtual async Task<TGetOutputDto> GetAsync(TKey id)
{
await CheckGetPolicyAsync();
var entity = await GetEntityByIdAsync(id);
return MapToGetOutputDto(entity);
}
public virtual async Task<PagedResultDto<TGetListOutputDto>> 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<TGetListOutputDto>(
totalCount,
entities.Select(MapToGetListOutputDto).ToList()
);
}
public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync();
@ -144,18 +110,6 @@ namespace Volo.Abp.Application.Services
protected abstract Task DeleteByIdAsync(TKey id);
protected abstract Task<TEntity> 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);
}
/// <summary>
/// Should apply sorting if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> 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;
}
/// <summary>
/// Applies sorting if no sorting specified but a limited result requested.
/// </summary>
/// <param name="query">The query.</param>
protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
{
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!");
}
/// <summary>
/// Should apply paging if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> 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;
}
/// <summary>
/// This method should create <see cref="IQueryable{TEntity}"/> based on given input.
/// It should filter query if needed, but should not do sorting or paging.
/// Sorting should be done in <see cref="ApplySorting"/> and paging should be done in <see cref="ApplyPaging"/>
/// methods.
/// </summary>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input)
{
return Repository;
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
}
/// <summary>
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
/// It uses <see cref="IObjectMapper"/> by default.
@ -284,18 +143,14 @@ namespace Volo.Abp.Application.Services
/// </summary>
protected virtual void SetIdForGuids(TEntity entity)
{
var entityWithGuidId = entity as IEntity<Guid>;
if (entityWithGuidId == null || entityWithGuidId.Id != Guid.Empty)
if (entity is IEntity<Guid> entityWithGuidId && entityWithGuidId.Id == Guid.Empty)
{
return;
EntityHelper.TrySetId(
entityWithGuidId,
() => GuidGenerator.Create(),
true
);
}
EntityHelper.TrySetId(
entityWithGuidId,
() => GuidGenerator.Create(),
true
);
}
/// <summary>

184
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<TEntity, TEntityDto, TKey>
: AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity
{
protected AbstractKeyReadOnlyAppService(IReadOnlyRepository<TEntity> repository)
: base(repository)
{
}
}
public abstract class AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TKey, TGetListInput>
: AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput>
where TEntity : class, IEntity
{
protected AbstractKeyReadOnlyAppService(IReadOnlyRepository<TEntity> repository)
: base(repository)
{
}
}
public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
: ApplicationService
, IReadOnlyAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
where TEntity : class, IEntity
{
protected IReadOnlyRepository<TEntity> ReadOnlyRepository { get; }
protected virtual string GetPolicyName { get; set; }
protected virtual string GetListPolicyName { get; set; }
protected AbstractKeyReadOnlyAppService(IReadOnlyRepository<TEntity> repository)
{
ReadOnlyRepository = repository;
}
public virtual async Task<TGetOutputDto> GetAsync(TKey id)
{
await CheckGetPolicyAsync();
var entity = await GetEntityByIdAsync(id);
return MapToGetOutputDto(entity);
}
public virtual async Task<PagedResultDto<TGetListOutputDto>> 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<TGetListOutputDto>(
totalCount,
entities.Select(MapToGetListOutputDto).ToList()
);
}
protected abstract Task<TEntity> GetEntityByIdAsync(TKey id);
protected virtual async Task CheckGetPolicyAsync()
{
await CheckPolicyAsync(GetPolicyName);
}
protected virtual async Task CheckGetListPolicyAsync()
{
await CheckPolicyAsync(GetListPolicyName);
}
/// <summary>
/// Should apply sorting if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> 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;
}
/// <summary>
/// Applies sorting if no sorting specified but a limited result requested.
/// </summary>
/// <param name="query">The query.</param>
protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
{
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!");
}
/// <summary>
/// Should apply paging if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> 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;
}
/// <summary>
/// This method should create <see cref="IQueryable{TEntity}"/> based on given input.
/// It should filter query if needed, but should not do sorting or paging.
/// Sorting should be done in <see cref="ApplySorting"/> and paging should be done in <see cref="ApplyPaging"/>
/// methods.
/// </summary>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input)
{
return ReadOnlyRepository;
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
}
}
}

2
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs

@ -70,7 +70,7 @@ namespace Volo.Abp.Application.Services
protected new IRepository<TEntity, TKey> Repository { get; }
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
: base(repository)
{
Repository = repository;
}

66
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<TEntity, TEntityDto, TKey>
: ReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected ReadOnlyAppService(IReadOnlyRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class ReadOnlyAppService<TEntity, TEntityDto, TKey, TGetListInput>
: ReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected ReadOnlyAppService(IReadOnlyRepository<TEntity, TKey> repository)
: base(repository)
{
}
}
public abstract class ReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
: AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected new IReadOnlyRepository<TEntity, TKey> Repository { get; }
protected ReadOnlyAppService(IReadOnlyRepository<TEntity, TKey> repository)
: base(repository)
{
Repository = repository;
}
protected override async Task<TEntity> GetEntityByIdAsync(TKey id)
{
return await Repository.GetAsync(id);
}
protected override IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
{
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime);
}
else
{
return query.OrderByDescending(e => e.Id);
}
}
}
}

1
framework/src/Volo.Abp.VirtualFileSystem/Volo.Abp.VirtualFileSystem.csproj

@ -17,6 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>

2
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();

29
framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/Embedded/EmbeddedFileSet.cs → 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<string, IFileInfo> Files => _files.Value;
private readonly Lazy<Dictionary<string, IFileInfo>> _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<Dictionary<string, IFileInfo>>(
CreateFiles,
true
);
}
public void AddFiles(Dictionary<string, IFileInfo> 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<string, IFileInfo> CreateFiles()
{
var files = new Dictionary<string, IFileInfo>(StringComparer.OrdinalIgnoreCase);
AddFiles(files);
return files;
}
}
}

22
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;
}
}
}

10
framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/IVirtualFileSet.cs

@ -1,10 +0,0 @@
using System.Collections.Generic;
using Microsoft.Extensions.FileProviders;
namespace Volo.Abp.VirtualFileSystem
{
public interface IVirtualFileSet
{
void AddFiles(Dictionary<string, IFileInfo> files);
}
}

19
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));
}
}
}

2
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))

52
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<string, IFileInfo> Files => _files.Value;
private readonly AbpVirtualFileSystemOptions _options;
private readonly Lazy<Dictionary<string, IFileInfo>> _files;
public InternalVirtualFileProvider(AbpVirtualFileSystemOptions options)
{
_options = options;
_files = new Lazy<Dictionary<string, IFileInfo>>(
CreateFiles,
true
);
}
private Dictionary<string, IFileInfo> CreateFiles()
{
var files = new Dictionary<string, IFileInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var set in _options.FileSets)
{
set.AddFiles(files);
}
return files;
}
protected override string NormalizePath(string subpath)
{
return VirtualFilePathHelper.NormalizePath(subpath);
}
}
}
}

15
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));
}
}
}

9
framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetList.cs

@ -2,13 +2,8 @@
namespace Volo.Abp.VirtualFileSystem
{
public class VirtualFileSetList : List<IVirtualFileSet>
public class VirtualFileSetList : List<VirtualFileSetInfo>
{
public List<string> PhysicalPaths { get; }
public VirtualFileSetList()
{
PhysicalPaths = new List<string>();
}
}
}

82
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<T>([NotNull] this VirtualFileSetList list, [CanBeNull] string baseNamespace = null, string baseFolderInProject = null)
public static void AddEmbedded<T>(
[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<T>([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<EmbeddedFileSet>().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<T>(
[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);
}
}
}
}

2
framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj

@ -4,6 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<RootNamespace />
</PropertyGroup>
@ -15,6 +16,7 @@
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

1
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

25
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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TestModule>("Volo.Abp.VirtualFileSystem.MyResources");
options.FileSets.AddEmbedded<TestModule>(
baseFolder: "/Volo/Abp/VirtualFileSystem/MyResources"
);
});
}
}

15
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;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxServiceNameLength { get; set; } = 256;
public const int MaxMethodNameLength = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxMethodNameLength { get; set; } = 128;
public const int MaxParametersLength = 2000;
/// <summary>
/// Default value: 2000
/// </summary>
public static int MaxParametersLength { get; set; } = 2000;
}
}

60
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;
/// <summary>
/// Default value: 96
/// </summary>
public static int MaxApplicationNameLength { get; set; } = 96;
public const int MaxClientIpAddressLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxClientIpAddressLength { get; set; } = 64;
public const int MaxClientNameLength = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxClientNameLength { get; set; } = 128;
public const int MaxClientIdLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxClientIdLength { get; set; } = 64;
public const int MaxCorrelationIdLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxCorrelationIdLength { get; set; } = 64;
public const int MaxBrowserInfoLength = 512;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxBrowserInfoLength { get; set; } = 512;
public const int MaxExceptionsLength = 4000;
/// <summary>
/// Default value: 4000
/// </summary>
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;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxCommentsLength { get; set; } = 256;
public const int MaxUrlLength = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxUrlLength { get; set; } = 256;
public const int MaxHttpMethodLength = 16;
/// <summary>
/// Default value: 16
/// </summary>
public static int MaxHttpMethodLength { get; set; } = 16;
public const int MaxUserNameLength = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxUserNameLength { get; set; } = 256;
public const int MaxTenantNameLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxTenantNameLength { get; set; } = 64;
}
}

10
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxEntityTypeFullNameLength { get; set; } = 128;
public const int MaxEntityIdLength = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxEntityIdLength { get; set; } = 128;
}
}

20
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;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxNewValueLength { get; set; } = 512;
public const int MaxOriginalValueLength = 512;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxOriginalValueLength { get; set; } = 512;
public const int MaxPropertyNameLength = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxPropertyNameLength { get; set; } = 128;
public const int MaxPropertyTypeFullNameLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxPropertyTypeFullNameLength { get; set; } = 64;
}
}

10
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxJobNameLength { get; set; } = 128;
public const int MaxJobArgsLength = 1024 * 1024;
/// <summary>
/// Default value: 1024 * 1024
/// </summary>
public static int MaxJobArgsLength { get; set; } = 1024 * 1024;
}
}

5
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;
/// <summary>
/// Default value: 128.
/// </summary>
public static int MaxNameLength { get; set; } = 128;
}
}

7
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; }
}

7
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; }
}
}

5
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
/// <summary>
/// Default value: 5242880
/// </summary>
public static int MaxFileSize { get; set; } = 5242880; //5MB
public static int MaxFileSizeAsMegabytes => Convert.ToInt32((MaxFileSize / 1024f) / 1024f);
}

9
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; }
}

15
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;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxNameLength { get; set; } = 256;
public const int MaxShortNameLength = 32;
/// <summary>
/// Default value: 32
/// </summary>
public static int MaxShortNameLength { get; set; } = 32;
public const int MaxDescriptionLength = 1024;
/// <summary>
/// Default value: 1024
/// </summary>
public static int MaxDescriptionLength { get; set; } = 1024;
}
}

5
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Comments/CommentConsts.cs

@ -2,6 +2,9 @@
{
public class CommentConsts
{
public const int MaxTextLength = 1024;
/// <summary>
/// Default value: 1024
/// </summary>
public static int MaxTextLength { get; set; } = 1024;
}
}

30
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;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxTitleLength { get; set; } = 512;
public const int MaxUrlLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxUrlLength { get; set; } = 64;
public const int MaxContentLength = 1024 * 1024; //1MB
/// <summary>
/// Default value: 1024 * 1024
/// </summary>
public static int MaxContentLength { get; set; } = 1024 * 1024; //1MB
public const int MaxDescriptionLength = 1000;
/// <summary>
/// Default value: 1000
/// </summary>
public static int MaxDescriptionLength { get; set; } = 1000;
public const int MaxTitleLengthToBeSeoFriendly = 60;
/// <summary>
/// Default value: 60
/// </summary>
public static int MaxTitleLengthToBeSeoFriendly { get; set; } = 60;
public const int MaxSeoFriendlyDescriptionLength = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int MaxSeoFriendlyDescriptionLength { get; set; } = 200;
}
}

10
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Tagging/TagConsts.cs

@ -2,8 +2,14 @@
{
public class TagConsts
{
public const int MaxNameLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxNameLength { get; set; } = 64;
public const int MaxDescriptionLength = 512;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxDescriptionLength { get; set; } = 512;
}
}

9
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; }

9
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; }
}

11
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; }

5
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; }
}
}

7
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; }
}
}

15
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]

13
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; }
}

7
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; }
}
}

6
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; }
}
}

7
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; }
}
}

7
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; }
}
}

5
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; }
}
}

5
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; }
}
}

53
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;
/// <summary>
/// Default value: 255
/// </summary>
public static int MaxNameLength { get; set; } = 255;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxVersionNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxLanguageCodeNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxFileNameNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxFormatNameLength { get; set; } = 128;
/// <summary>
/// Default value: 2048
/// </summary>
public static int MaxEditLinkLength { get; set; } = 2048;
/// <summary>
/// Default value: 2048
/// </summary>
public static int MaxRootUrlLength { get; set; } = 2048;
/// <summary>
/// Default value: 2048
/// </summary>
public static int MaxRawRootUrlLength { get; set; } = 2048;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxLocalDirectoryLength { get; set; } = 512;
}
}

5
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;
/// <summary>
/// Default value: 10
/// </summary>
public static int MaxLanguageCodeLength { get; set; } = 10;
}
}

41
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
/// <summary>
/// Default value: 32
/// </summary>
public static int MaxShortNameLength { get; set; } = 32;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxDefaultDocumentNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNavigationDocumentNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxParametersDocumentNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxLatestVersionBranchNameLength { get; set; } = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxVersionNameLength { get; set; } = 128;
}
}

20
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set;} = 128;
public const int MaxProviderNameLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderNameLength { get; set;} = 64;
public const int MaxProviderKeyLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderKeyLength { get; set;} = 64;
public const int MaxValueLength = 128;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxValueLength { get; set;} = 128;
}
}

19
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityClaimTypeConsts.cs

@ -2,10 +2,29 @@
{
public class IdentityClaimTypeConsts
{
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxNameLength { get; set; } = 256;
/// <summary>
/// Default value: 512
/// </summary>
public static int MaxRegexLength { get; set; } = 512;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxRegexDescriptionLength { get; set; } = 128;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxDescriptionLength { get; set; } = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxConcurrencyStampLength { get; set; } = 256;
}
}

11
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityRoleConsts.cs

@ -2,8 +2,19 @@
{
public static class IdentityRoleConsts
{
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxNameLength { get; set; } = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxNormalizedNameLength { get; set; } = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxConcurrencyStampLength { get; set; } = 256;
}
}

6
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserClaimConsts.cs

@ -2,8 +2,14 @@
{
public static class IdentityUserClaimConsts
{
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxClaimTypeLength { get; set; } = 256;
/// <summary>
/// Default value: 1024
/// </summary>
public static int MaxClaimValueLength { get; set; } = 1024;
}
}

14
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs

@ -18,12 +18,24 @@ namespace Volo.Abp.Identity
public static int MaxPhoneNumberLength { get; set; } = AbpUserConsts.MaxPhoneNumberLength;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxPasswordLength { get; set; } = 128;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxPasswordHashLength { get; set; } = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxSecurityStampLength { get; set; } = 256;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxConcurrencyStampLength { get; set; } = 256;
}
}

11
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserLoginConsts.cs

@ -2,8 +2,19 @@
{
public static class IdentityUserLoginConsts
{
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxLoginProviderLength { get; set; } = 64;
/// <summary>
/// Default value: 196
/// </summary>
public static int MaxProviderKeyLength { get; set; } = 196;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxProviderDisplayNameLength { get; set; } = 128;
}
}

6
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserTokenConsts.cs

@ -2,8 +2,14 @@
{
public static class IdentityUserTokenConsts
{
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxLoginProviderLength { get; set; } = 64;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
}
}

5
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int ProviderMaxLength { get; set; } = 200;
}
}

5
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int PostLogoutRedirectUriMaxLength { get; set; } = 2000;
public static int PostLogoutRedirectUriMaxLengthValue { get; set; } = 2000;
}
}

11
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;
/// <summary>
/// Default value: 250
/// </summary>
public static int KeyMaxLength { get; set; } = 250;
/// <summary>
/// Default value: 2000
/// </summary>
public static int ValueMaxLength { get; set; } = 2000;
}
}

5
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;
/// <summary>
/// Default value: 2000
/// </summary>
public static int RedirectUriMaxLength { get; set; } = 2000;
public static int RedirectUriMaxLengthValue { get; set; } = 2000;
}
}

5
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int ScopeMaxLength { get; set; } = 200;
}
}

35
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int KeyMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 50
/// </summary>
public static int TypeMaxLength { get; set; } = 50;
/// <summary>
/// Default value: 200
/// </summary>
public static int SubjectIdMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int ClientIdMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 50000
/// </summary>
public static int DataMaxLength { get; set; } = 50000;
/// <summary>
/// Default value: 50000
/// </summary>
public static int DataMaxLengthValue { get; set; } = 50000;
}
}

17
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int NameMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int DisplayNameMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int DescriptionMaxLength { get; set; } = 1000;
}
}

18
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;
/// <summary>
/// Default value: 250
/// </summary>
public static int TypeMaxLength { get; set; } = 250;
/// <summary>
/// Default value: 4000
/// </summary>
public static int ValueMaxLength { get; set; } = 4000;
public static int ValueMaxLengthValue { get; set; } = ValueMaxLength;
public const int DescriptionMaxLength = 2000;
/// <summary>
/// Default value: 2000
/// </summary>
public static int DescriptionMaxLength { get; set; } = 2000;
}
}

5
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;
/// <summary>
/// Default value: 200
/// </summary>
public static int TypeMaxLength { get; set; } = 200;
}
}

15
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
public const int MaxProviderNameLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderNameLength { get; set; } = 64;
public const int MaxProviderKeyLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderKeyLength { get; set; } = 64;
}
}

24
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;
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
/// <summary>
/// Default value: 2048
/// </summary>
public static int MaxValueLength { get; set; } = 2048;
public static int MaxValueLengthValue { get; set; } = MaxValueLength;
public const int MaxProviderNameLength = 64;
public const int MaxProviderKeyLength = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderNameLength { get; set; } = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxProviderKeyLength { get; set; } = 64;
}
}

3
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; }
}
}

10
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;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxNameLength { get; set; } = 64;
public const int MaxValueLength = 1024;
/// <summary>
/// Default value: 1024
/// </summary>
public static int MaxValueLength { get; set; } = 1024;
}
}

5
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;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxNameLength { get; set; } = 64;
}
}

3
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; }
}
}

3
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]

3
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; }
}

15
modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs

@ -2,14 +2,29 @@ namespace Volo.Abp.Users
{
public class AbpUserConsts
{
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxUserNameLength { get; set; } = 256;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxNameLength { get; set; } = 64;
/// <summary>
/// Default value: 64
/// </summary>
public static int MaxSurnameLength { get; set; } = 64;
/// <summary>
/// Default value: 256
/// </summary>
public static int MaxEmailLength { get; set; } = 256;
/// <summary>
/// Default value: 16
/// </summary>
public static int MaxPhoneNumberLength { get; set; } = 16;
}
}

4
npm/ng-packs/packages/theme-shared/src/lib/components/table/table.component.ts

@ -11,6 +11,10 @@ import {
ViewEncapsulation,
} from '@angular/core';
/**
*
* @deprecated use ngx-datatale instead.
*/
@Component({
selector: 'abp-table',
templateUrl: 'table.component.html',

7
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj

@ -5,8 +5,9 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.Domain.Shared\Volo.Abp.Identity.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identityserver\src\Volo.Abp.IdentityServer.Domain.Shared\Volo.Abp.IdentityServer.Domain.Shared.csproj" />
@ -23,4 +24,8 @@
<Content Remove="Localization\MyProjectName\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
</Project>

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs

@ -35,7 +35,7 @@ namespace MyCompanyName.MyProjectName
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyProjectNameDomainSharedModule>("MyCompanyName.MyProjectName");
options.FileSets.AddEmbedded<MyProjectNameDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs

@ -77,8 +77,6 @@ namespace MyCompanyName.MyProjectName
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<MyProjectNameDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}MyCompanyName.MyProjectName.Domain.Shared"));
options.FileSets.ReplaceEmbeddedByPhysical<MyProjectNameDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}MyCompanyName.MyProjectName.Domain"));
//<TEMPLATE-REMOVE>
options.FileSets.ReplaceEmbeddedByPhysical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
@ -87,6 +85,8 @@ namespace MyCompanyName.MyProjectName
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBasicThemeModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}..{0}modules{0}account{0}src{0}Volo.Abp.Account.Web", Path.DirectorySeparatorChar)));
//</TEMPLATE-REMOVE>
options.FileSets.ReplaceEmbeddedByPhysical<MyProjectNameDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}MyCompanyName.MyProjectName.Domain.Shared"));
options.FileSets.ReplaceEmbeddedByPhysical<MyProjectNameDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}MyCompanyName.MyProjectName.Domain"));
});
}

2
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/tr.json

@ -1,6 +1,6 @@
{
"culture": "tr",
"texts": {
"ManageYourProfile": "Profil yönetimi"
"ManageYourProfile": "Profil yönetimi"
}
}

5
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj

@ -5,12 +5,17 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\MyProjectName\*.json" />
<Content Remove="Localization\MyProjectName\*.json" />

2
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs

@ -17,7 +17,7 @@ namespace MyCompanyName.MyProjectName
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyProjectNameDomainSharedModule>("MyCompanyName.MyProjectName");
options.FileSets.AddEmbedded<MyProjectNameDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>

Loading…
Cancel
Save