diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index a5089e06f7..d0d829bbf3 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -13,8 +13,11 @@ "Permission:Edit": "Edit", "Permission:Delete": "Delete", "Permission:Create": "Create", + "Permission:Accounting": "Accounting", + "Permission:Accounting:Quotation": "Quotation", + "Permission:Accounting:Invoice": "Invoice", "Menu:Organizations": "Organizations", - "Menu:Accounting": "Accounting", + "Menu:Accounting": "Accounting", "Menu:Packages": "Packages", "NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?", "NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?", @@ -94,13 +97,28 @@ "UsernameOrEmail": "Username or email", "UsernameOrEmailPlaceholder": "Username or email...", "Member": "Member", - "QuotationPurchasedOrderNo": "Quotation Purchased Order No", - "QuotationTime": "Quotation Time", - "CompanyName": "Company Name", - "CompanyAddress": "Company Address", + "PurchaseOrderNo": "Purchase order no", + "QuotationDate": "Quotation date", + "CompanyName": "Company name", + "CompanyAddress": "Company address", "Price": "Price", + "DiscountText": "Discount text", + "DiscountQuantity": "Discount quantity", + "DiscountPrice": "Discount price", + "Quotation": "Quotation", "ExtraText": "Extra Text", "ExtraAmount": "Extra Amount", - "DownloadQuotation": "Download Quotation" + "DownloadQuotation": "Download Quotation", + "Invoice": "Invoice", + "TaxNumber": "Tax Number", + "InvoiceNumber": "Invoice Number", + "InvoiceDate": "Invoice Date", + "Quantity": "Quantity", + "AddProduct": "Add Product", + "AddProductWarning": "You need to add product!", + "TotalPrice": "Total Price", + "Generate": "Generate", + "MissingQuantityField": "The quantity field is required!", + "MissingPriceField": "The Price field is required!" } } \ No newline at end of file diff --git a/common.DotSettings b/common.DotSettings index 0eb4875d49..6f40d029a7 100644 --- a/common.DotSettings +++ b/common.DotSettings @@ -20,5 +20,11 @@ False False SQL + False + False + False + False + False + False True \ No newline at end of file diff --git a/docs/en/CLI.md b/docs/en/CLI.md index b687a5244a..75e3e027b4 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -135,6 +135,8 @@ abp update [options] * `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. * `--npm`: Only updates NPM packages. * `--nuget`: Only updates NuGet packages. +* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default +* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. ### switch-to-preview @@ -170,7 +172,11 @@ Some features of the CLI requires to be logged in to abp.io platform. To login w abp login ``` -Notice that, a new login with an already active session, will kill the previous session and creates a new one. +```bash +abp login -p +``` + +Notice that, a new login with an already active session, overwrites the previous session. ### logout diff --git a/docs/en/Getting-Started.md b/docs/en/Getting-Started.md index 07e55f553e..72e2f2886c 100644 --- a/docs/en/Getting-Started.md +++ b/docs/en/Getting-Started.md @@ -400,8 +400,8 @@ Enter **admin** as the username and **1q2w3E*** as the password to login to the The application is up and running. You can continue to develop your application based on this startup template. -> The [application startup template](startup-templates/application/index.md) includes the TenantManagement and Identity modules. +> The [application startup template](Startup-Templates/Application.md) includes the TenantManagement and Identity modules. ## What's next? -[Application development tutorial](tutorials/book-store/part-1.md) +[Application development tutorial](Tutorials/Part-1.md) diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index a0aa6612e9..c684d1fc1c 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -38,7 +38,7 @@ Create a new project named `Acme.BookStore` where `Acme` is the company name and #### Create the project -By running the below command, it creates a new ABP project with the database provider `{{DB_Text}}` and UI option `MVC`. To see the other CLI options, check out [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) document. +By running the below command, it creates a new ABP project with the database provider `{{DB_Text}}` and UI option `{{UI_Value}}`. To see the other CLI options, check out [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) document. ```bash abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none @@ -61,7 +61,7 @@ After creating the project, you need to apply the initial migrations and create To run the project, right click to the {{if UI == "MVC"}} `Acme.BookStore.Web`{{end}} {{if UI == "NG"}} `Acme.BookStore.HttpApi.Host` {{end}} project and click **Set As StartUp Project**. And run the web project by pressing **CTRL+F5** (*without debugging and fast*) or press **F5** (*with debugging and slow*). {{if UI == "NG"}}You will see the Swagger UI for BookStore API.{{end}} -Further information, see the [running the application section](../../Getting-Started-{{if UI == "NG"}}Angular{{else}}AspNetCore-MVC{{end}}-Template#running-the-application).Getting-Started-AspNetCore-MVC-Template#running-the-application +Further information, see the [running the application section](../Getting-Started?UI={{UI}}#run-the-application). ![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png) @@ -335,7 +335,7 @@ INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES ### Create the application service -The next step is to create an [application service](../../Application-Services.md) to manage the books which will allow us the four basic functions: creating, reading, updating and deleting. Application layer is separated into two projects: +The next step is to create an [application service](../Application-Services.md) to manage the books which will allow us the four basic functions: creating, reading, updating and deleting. Application layer is separated into two projects: * `Acme.BookStore.Application.Contracts` mainly contains your `DTO`s and application service interfaces. * `Acme.BookStore.Application` contains the implementations of your application services. @@ -874,97 +874,52 @@ We'll see **book-list works!** text on the books page: Run the following command in the terminal to create a new state, named `BooksState`: -![Initial book list page](./images/bookstore-generate-state-books.png) - ```bash -yarn ng generate ngxs-schematic:state books +npx @ngxs/cli --name books --directory src/app/books ``` -* This command creates several new files and updates `app.modules.ts` file to import the `NgxsModule` with the new state. - -#### Get books data from backend - -Create data types to map the data from the backend (you can check Swagger UI or your backend API to see the data format). +* This command creates `books.state.ts` and `books.actions.ts` files in the `src/app/books/state` folder. See the [NGXS CLI documentation](https://www.ngxs.io/plugins/cli). -![BookDto properties](./images/bookstore-swagger-book-dto-properties.png) - -Open the `books.ts` file in the `app\store\models` folder and replace the content as below: +Import the `BooksState` to the `app.module.ts` in the `src/app` folder and then add the `BooksState` to `forRoot` static method of `NgxsModule` as an array element of the first parameter of the method. ```js -export namespace Books { - export interface State { - books: Response; - } +// ... +import { BooksState } from './books/state/books.state'; //<== imported BooksState ==> - export interface Response { - items: Book[]; - totalCount: number; - } +@NgModule({ + imports: [ + // other imports - export interface Book { - name: string; - type: BookType; - publishDate: string; - price: number; - lastModificationTime: string; - lastModifierId: string; - creationTime: string; - creatorId: string; - id: string; - } + NgxsModule.forRoot([BooksState]), //<== added BooksState ==> - export enum BookType { - Undefined, - Adventure, - Biography, - Dystopia, - Fantastic, - Horror, - Science, - ScienceFiction, - Poetry, - } -} + //other imports + ], + // ... +}) +export class AppModule {} ``` -* Added `Book` interface that represents a book object and `BookType` enum which represents a book category. +#### Generate proxies -#### BooksService +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) -Create a new service, named `BooksService` to perform `HTTP` calls to the server: +Run the following command in the `angular` folder: ```bash -yarn ng generate service books/shared/books +abp generate-proxy --module app ``` -![service-terminal-output](./images/bookstore-service-terminal-output.png) +![Generate proxy command](./images/generate-proxy-command.png) -Open the `books.service.ts` file in `app\books\shared` folder and replace the content as below: +The generated files looks like below: -```js -import { Injectable } from '@angular/core'; -import { RestService } from '@abp/ng.core'; -import { Books } from '../../store/models'; -import { Observable } from 'rxjs'; +![Generated files](./images/generated-proxies.png) -@Injectable({ - providedIn: 'root', -}) -export class BooksService { - constructor(private restService: RestService) {} - - get(): Observable { - return this.restService.request({ - method: 'GET', - url: '/api/app/book' - }); - } -} -``` +#### GetBooks Action -* We added the `get` method to get the list of books by performing an HTTP request to the related endpoint. +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`books.actions.ts` file in `app\store\actions` folder and replace the content below: +Open the `books.actions.ts` file in `app/books/state` folder and replace the content below: ```js export class GetBooks { @@ -974,43 +929,48 @@ export class GetBooks { #### Implement BooksState -Open the `books.state.ts` file in `app\store\states` folder and replace the content below: +Open the `books.state.ts` file in `app/books/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 '../actions/books.actions'; -import { Books } from '../models/books'; -import { BooksService } from '../../books/shared/books.service'; +import { GetBooks } from './books.actions'; +import { BookService } from '../../app/shared/services'; import { tap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; -@State({ +export class BooksStateModel { + public book: PagedResultDto; +} + +@State({ name: 'BooksState', - defaults: { books: {} } as Books.State, + defaults: { book: {} } as BooksStateModel, }) @Injectable() export class BooksState { @Selector() - static getBooks(state: Books.State) { - return state.books.items || []; + static getBooks(state: BooksStateModel) { + return state.book.items || []; } - constructor(private booksService: BooksService) {} + constructor(private bookService: BookService) {} @Action(GetBooks) - get(ctx: StateContext) { - return this.booksService.get().pipe( - tap(booksResponse => { + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((booksResponse) => { ctx.patchState({ - books: booksResponse, + book: booksResponse, }); - }), + }) ); } } ``` - -* We added the `GetBooks` action that retrieves the books data via `BooksService` and patches the state. +* We added the book property to BooksStateModel model. +* We added the `GetBooks` action that retrieves the books data via `BooksService` that generated via ABP CLI and patches the state. * `NGXS` requires to return the observable without subscribing it in the get function. #### BookListComponent @@ -1019,11 +979,12 @@ Open the `book-list.component.ts` file in `app\books\book-list` folder and repla ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks } from '../../store/actions'; +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; @Component({ selector: 'app-book-list', @@ -1032,13 +993,13 @@ import { GetBooks } from '../../store/actions'; }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; loading = false; - constructor(private store: Store) { } + constructor(private store: Store) {} ngOnInit() { this.get(); @@ -1046,9 +1007,10 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } } ``` diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 9565724cb8..cfe9936812 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -456,161 +456,76 @@ 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. -#### Type definition - -Open `books.ts` file in `app\store\models` folder and replace the content as below: - -```js -export namespace Books { - export interface State { - books: Response; - } - - export interface Response { - items: Book[]; - totalCount: number; - } - - export interface Book { - name: string; - type: BookType; - publishDate: string; - price: number; - lastModificationTime: string; - lastModifierId: string; - creationTime: string; - creatorId: string; - id: string; - } - - export enum BookType { - Undefined, - Adventure, - Biography, - Dystopia, - Fantastic, - Horror, - Science, - ScienceFiction, - Poetry, - } - - //<== added CreateUpdateBookInput interface ==> - export interface CreateUpdateBookInput { - name: string; - type: BookType; - publishDate: string; - price: number; - } -} -``` - -* We added `CreateUpdateBookInput` interface. -* You can see the properties of this interface from Swagger UI. -* The `CreateUpdateBookInput` interface matches with the `CreateUpdateBookDto` in the backend. - -#### Service method - -Open the `books.service.ts` file in `app\books\shared` folder and replace the content as below: - -```js -import { Injectable } from '@angular/core'; -import { RestService } from '@abp/ng.core'; -import { Books } from '../../store/models'; -import { Observable } from 'rxjs'; - -@Injectable({ - providedIn: 'root', -}) -export class BooksService { - constructor(private restService: RestService) {} - - get(): Observable { - return this.restService.request({ - method: 'GET', - url: '/api/app/book' - }); - } - - //<== added create method ==> - create(createBookInput: Books.CreateUpdateBookInput): Observable { - return this.restService.request({ - method: 'POST', - url: '/api/app/book', - body: createBookInput - }); - } -} -``` - -- We added the `create` method to perform an HTTP Post request to the server. -- `restService.request` function gets generic parameters for the types sent to and received from the server. This example sends a `CreateUpdateBookInput` object and receives a `Book` object (you can set `void` for request or return type if not used). - #### State definitions -Open `books.action.ts` in `app\store\actions` folder and replace the content as below: +Open `books.action.ts` in `books\state` folder and replace the content as below: ```js -import { Books } from '../models'; //<== added this line ==> +import { CreateUpdateBookDto } from '../../app/shared/models'; //<== added this line ==> export class GetBooks { static readonly type = '[Books] Get'; } -//added CreateUpdateBook class +// added CreateUpdateBook class export class CreateUpdateBook { static readonly type = '[Books] Create Update Book'; - constructor(public payload: Books.CreateUpdateBookInput) { } + constructor(public payload: CreateUpdateBookDto) { } } ``` -* We imported the Books namespace and created the `CreateUpdateBook` action. +* We imported the `CreateUpdateBookDto` model and created the `CreateUpdateBook` action. -Open `books.state.ts` file in `app\store\states` and replace the content as below: +Open `books.state.ts` file in `books\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 } from '../actions/books.actions'; //<== added CreateUpdateBook==> -import { Books } from '../models/books'; -import { BooksService } from '../../books/shared/books.service'; +import { GetBooks, CreateUpdateBook } from './books.actions'; // <== added CreateUpdateBook==> +import { BookService } from '../../app/shared/services'; import { tap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; + +export class BooksStateModel { + public book: PagedResultDto; +} -@State({ +@State({ name: 'BooksState', - defaults: { books: {} } as Books.State, + defaults: { book: {} } as BooksStateModel, }) @Injectable() export class BooksState { @Selector() - static getBooks(state: Books.State) { - return state.books.items || []; + static getBooks(state: BooksStateModel) { + return state.book.items || []; } - constructor(private booksService: BooksService) { } + constructor(private bookService: BookService) {} @Action(GetBooks) - get(ctx: StateContext) { - return this.booksService.get().pipe( - tap(booksResponse => { + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((bookResponse) => { ctx.patchState({ - books: booksResponse, + book: bookResponse, }); - }), + }) ); } - //added CreateUpdateBook action listener + // added CreateUpdateBook action listener @Action(CreateUpdateBook) - save(ctx: StateContext, action: CreateUpdateBook) { - return this.booksService.create(action.payload); + save(ctx: StateContext, action: CreateUpdateBook) { + return this.bookService.createByInput(action.payload); } } ``` * 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 `create` method of the `BooksService`. +When the `SaveBook` action dispatched, the save method is being executed. It calls `createByInput` method of the `BookService`. #### Add a modal to BookListComponent @@ -694,11 +609,12 @@ Open `book-list.component.ts` file in `books\book-list` folder and replace the c ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks } from '../../store/actions'; +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; @Component({ selector: 'app-book-list', @@ -707,15 +623,15 @@ import { GetBooks } from '../../store/actions'; }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; loading = false; - isModalOpen = false; //<== added this line ==> + isModalOpen = false; // <== added this line ==> - constructor(private store: Store) { } + constructor(private store: Store) {} ngOnInit() { this.get(); @@ -723,12 +639,13 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } - //added createBook method + // added createBook method createBook() { this.isModalOpen = true; } @@ -749,12 +666,13 @@ Open `book-list.component.ts` file in `app\books\book-list` folder and replace t ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks } from '../../store/actions'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; //<== added this line ==> +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // <== added this line ==> @Component({ selector: 'app-book-list', @@ -763,17 +681,17 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms'; //<== added }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; loading = false; isModalOpen = false; - form: FormGroup; + form: FormGroup; // <== added this line ==> - constructor(private store: Store, private fb: FormBuilder) { } //<== added FormBuilder ==> + constructor(private store: Store, private fb: FormBuilder) {} // <== added FormBuilder ==> ngOnInit() { this.get(); @@ -781,9 +699,10 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } createBook() { @@ -791,7 +710,7 @@ export class BookListComponent implements OnInit { this.isModalOpen = true; } - //added buildForm method + // added buildForm method buildForm() { this.form = this.fb.group({ name: ['', Validators.required], @@ -804,6 +723,7 @@ export class BookListComponent implements OnInit { ``` * We imported `FormGroup, FormBuilder and Validators`. +* We added `form: FormGroup` variable. * We injected `fb: FormBuilder` service to the constructor. The [FormBuilder](https://angular.io/api/forms/FormBuilder) service provides convenient methods for generating controls. It reduces the amount of boilerplate needed to build complex forms. * We added `buildForm` method to the end of the file and executed `buildForm()` in the `createBook` method. This method creates a reactive form to be able to create a new book. * The `group` method of `FormBuilder`, `fb` creates a `FormGroup`. @@ -880,35 +800,34 @@ export class BooksModule { } * We imported `NgbDatepickerModule` to be able to use the date picker. - - Open `book-list.component.ts` file in `app\books\book-list` folder and replace the content as below: ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks } from '../../store/actions'; +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; //<== added this line ==> +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; // <== added this line ==> @Component({ selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }] //<== added this line ==> + providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], // <== added this line ==> }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; //added bookTypeArr array - bookTypeArr = Object.keys(Books.BookType).filter( - bookType => typeof this.booksType[bookType] === 'number' + bookTypeArr = Object.keys(BookType).filter( + (bookType) => typeof this.booksType[bookType] === 'number' ); loading = false; @@ -917,7 +836,7 @@ export class BookListComponent implements OnInit { form: FormGroup; - constructor(private store: Store, private fb: FormBuilder) { } + constructor(private store: Store, private fb: FormBuilder) {} ngOnInit() { this.get(); @@ -925,9 +844,10 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } createBook() { @@ -965,35 +885,16 @@ Now, you can open your browser to see the changes: #### Saving the book -Open `book-list.component.html` in `app\books\book-list` folder and add the following `abp-button` to save the new book. - -```html - - - - - - -``` - -* This adds a save button to the bottom area of the modal: - -![Save button to the modal](./images/bookstore-new-book-form-v2.png) - Open `book-list.component.ts` file in `app\books\book-list` folder and replace the content as below: ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks, CreateUpdateBook } from '../../store/actions'; //<== added CreateUpdateBook ==> +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks, CreateUpdateBook } from '../state/books.actions'; // <== added CreateUpdateBook ==> +import { BooksState } from '../state/books.state'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; @@ -1001,16 +902,17 @@ 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: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; - bookTypeArr = Object.keys(Books.BookType).filter( - bookType => typeof this.booksType[bookType] === 'number' + //added bookTypeArr array + bookTypeArr = Object.keys(BookType).filter( + (bookType) => typeof this.booksType[bookType] === 'number' ); loading = false; @@ -1019,7 +921,7 @@ export class BookListComponent implements OnInit { form: FormGroup; - constructor(private store: Store, private fb: FormBuilder) { } + constructor(private store: Store, private fb: FormBuilder) {} ngOnInit() { this.get(); @@ -1027,9 +929,10 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } createBook() { @@ -1062,37 +965,46 @@ export class BookListComponent implements OnInit { ``` * We imported `CreateUpdateBook`. -* We added `save` method +* We added `save` method -### Updating an existing book - -#### BooksService +Open `book-list.component.html` in `app\books\book-list` folder and add the following `abp-button` to save the new book. -Open the `books.service.ts` in `app\books\shared` folder and add the `getById` and `update` methods. +```html + + + + + + +``` -```js -getById(id: string): Observable { - return this.restService.request({ - method: 'GET', - url: `/api/app/book/${id}` - }); -} +Find the `
` tag and replace below content: -update(updateBookInput: Books.CreateUpdateBookInput, id: string): Observable { - return this.restService.request({ - method: 'PUT', - url: `/api/app/book/${id}`, - body: updateBookInput - }); -} +```html + ``` + +* We added the `(ngSubmit)="save()"` to `` element to save a new book by pressing the enter. +* We added `abp-button` to the bottom area of the modal to save a new book. + +The final modal UI looks like below: + +![Save button to the modal](./images/bookstore-new-book-form-v2.png) + +### Updating a book + #### CreateUpdateBook action -Open the `books.actions.ts` in `app\store\actions` folder and replace the content as below: +Open the `books.actions.ts` in `books\state` folder and replace the content as below: ```js -import { Books } from '../models'; +import { CreateUpdateBookDto } from '../../app/shared/models'; export class GetBooks { static readonly type = '[Books] Get'; @@ -1100,54 +1012,55 @@ export class GetBooks { export class CreateUpdateBook { static readonly type = '[Books] Create Update Book'; - constructor(public payload: Books.CreateUpdateBookInput, public id?: string) { } //<== added id parameter ==> + constructor(public payload: CreateUpdateBookDto, public id?: string) { } // <== added id parameter ==> } ``` * We added `id` parameter to the `CreateUpdateBook` action's constructor. -Open the `books.state.ts` in `app\store\states` folder and replace the `save` method as below: +Open the `books.state.ts` in `books\state` folder and replace the `save` method as below: ```js @Action(CreateUpdateBook) -save(ctx: StateContext, action: CreateUpdateBook) { - if (action.id) { - return this.booksService.update(action.payload, action.id); - } else { - return this.booksService.create(action.payload); - } +save(ctx: StateContext, action: CreateUpdateBook) { + if (action.id) { + return this.bookService.updateByIdAndInput(action.payload, action.id); + } else { + return this.bookService.createByInput(action.payload); + } } ``` #### BookListComponent -Open `book-list.component.ts` in `app\books\book-list` folder and inject `BooksService` dependency by adding it to the constructor and add a variable named `selectedBook`. +Open `book-list.component.ts` in `app\books\book-list` folder and inject `BookService` dependency by adding it to the constructor and add a variable named `selectedBook`. ```js import { Component, OnInit } from '@angular/core'; -import { Store, Select } from '@ngxs/store'; -import { BooksState } from '../../store/states'; +import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Books } from '../../store/models'; -import { GetBooks, CreateUpdateBook } from '../../store/actions'; +import { finalize } from 'rxjs/operators'; +import { BookDto, BookType } from '../../app/shared/models'; +import { GetBooks, CreateUpdateBook } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; -import { BooksService } from '../shared/books.service'; //<== imported BooksService ==> +import { BookService } from '../../app/shared/services'; // <== imported BookService ==> @Component({ selector: 'app-book-list', templateUrl: './book-list.component.html', styleUrls: ['./book-list.component.scss'], - providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }] + providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], }) export class BookListComponent implements OnInit { @Select(BooksState.getBooks) - books$: Observable; + books$: Observable; - booksType = Books.BookType; + booksType = BookType; - bookTypeArr = Object.keys(Books.BookType).filter( - bookType => typeof this.booksType[bookType] === 'number' + bookTypeArr = Object.keys(BookType).filter( + (bookType) => typeof this.booksType[bookType] === 'number' ); loading = false; @@ -1156,9 +1069,9 @@ export class BookListComponent implements OnInit { form: FormGroup; - selectedBook = {} as Books.Book; //<== declared selectedBook ==> + selectedBook = {} as BookDto; // <== declared selectedBook ==> - constructor(private store: Store, private fb: FormBuilder, private booksService: BooksService) { } + constructor(private store: Store, private fb: FormBuilder, private bookService: BookService) {} //<== injected BookService ==> ngOnInit() { this.get(); @@ -1166,39 +1079,38 @@ export class BookListComponent implements OnInit { get() { this.loading = true; - this.store.dispatch(new GetBooks()).subscribe(() => { - this.loading = false; - }); + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); } - //<== this method is replaced ==> + // <== this method is replaced ==> createBook() { - this.selectedBook = {} as Books.Book; //<== added ==> + this.selectedBook = {} as BookDto; // <== added ==> this.buildForm(); this.isModalOpen = true; } - //<== added editBook method ==> + // <== added editBook method ==> editBook(id: string) { - this.booksService.getById(id).subscribe(book => { + this.bookService.getById(id).subscribe((book) => { this.selectedBook = book; this.buildForm(); this.isModalOpen = true; }); } - //<== this method is replaced ==> + // <== this method is replaced ==> buildForm() { this.form = this.fb.group({ - name: [this.selectedBook.name || "", Validators.required], + name: [this.selectedBook.name || '', Validators.required], type: [this.selectedBook.type || null, Validators.required], publishDate: [ - this.selectedBook.publishDate - ? new Date(this.selectedBook.publishDate) - : null, - Validators.required + this.selectedBook.publishDate ? new Date(this.selectedBook.publishDate) : null, + Validators.required, ], - price: [this.selectedBook.price || null, Validators.required] + price: [this.selectedBook.price || null, Validators.required], }); } @@ -1208,7 +1120,8 @@ export class BookListComponent implements OnInit { } //<== added this.selectedBook.id ==> - this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id)) + this.store + .dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id)) .subscribe(() => { this.isModalOpen = false; this.form.reset(); @@ -1218,9 +1131,9 @@ export class BookListComponent implements OnInit { } ``` -* We imported `BooksService`. -* We declared a variable named `selectedBook` as `Books.Book`. -* We injected `BooksService` to the constructor. `BooksService` is being used to retrieve the book data which is being edited. +* 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. @@ -1298,24 +1211,9 @@ Open `book-list.component.html` in `app\books\book-list` folder and find the ` { - return this.restService.request({ - method: 'DELETE', - url: `/api/app/book/${id}` - }); -} -``` - -* `Delete` method gets `id` parameter and makes a `DELETE` HTTP request to the relevant endpoint. - #### DeleteBook action -Open `books.actions.ts` in `app\store\actions `folder and add an action named `DeleteBook`. +Open `books.actions.ts` in `books\state `folder and add an action named `DeleteBook`. ```js export class DeleteBook { @@ -1324,53 +1222,58 @@ export class DeleteBook { } ``` -Open the `books.state.ts` in `app\store\states` folder and replace the content as below: +Open the `books.state.ts` in `books\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 '../actions/books.actions'; //<== added DeleteBook==> -import { Books } from '../models/books'; -import { BooksService } from '../../books/shared/books.service'; +import { GetBooks, CreateUpdateBook, DeleteBook } from './books.actions'; // <== added DeleteBook==> +import { BookService } from '../../app/shared/services'; import { tap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; + +export class BooksStateModel { + public book: PagedResultDto; +} -@State({ +@State({ name: 'BooksState', - defaults: { books: {} } as Books.State, + defaults: { book: {} } as BooksStateModel, }) @Injectable() export class BooksState { @Selector() - static getBooks(state: Books.State) { - return state.books.items || []; + static getBooks(state: BooksStateModel) { + return state.book.items || []; } - constructor(private booksService: BooksService) { } + constructor(private bookService: BookService) {} @Action(GetBooks) - get(ctx: StateContext) { - return this.booksService.get().pipe( - tap(booksResponse => { + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((booksResponse) => { ctx.patchState({ - books: booksResponse, + book: booksResponse, }); - }), + }) ); } @Action(CreateUpdateBook) - save(ctx: StateContext, action: CreateUpdateBook) { + save(ctx: StateContext, action: CreateUpdateBook) { if (action.id) { - return this.booksService.update(action.payload, action.id); + return this.bookService.updateByIdAndInput(action.payload, action.id); } else { - return this.booksService.create(action.payload); + return this.bookService.createByInput(action.payload); } } - //<== added DeleteBook ==> + // <== added DeleteBook action listener ==> @Action(DeleteBook) - delete(ctx: StateContext, action: DeleteBook) { - return this.booksService.delete(action.id); + delete(ctx: StateContext, action: DeleteBook) { + return this.bookService.deleteById(action.id); } } ``` @@ -1379,27 +1282,8 @@ export class BooksState { - We added `DeleteBook` action listener to the end of the file. - - -#### Add a delete button - - -Open `book-list.component.html` in `app\books\book-list` folder and modify the `ngbDropdownMenu` to add the delete button as shown below: - -```html -
- - -
-``` - -The final actions dropdown UI looks like below: -![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png) - -#### Delete confirmation dialog +#### Delete confirmation popup Open `book-list.component.ts` in`app\books\book-list` folder and inject the `ConfirmationService`. @@ -1410,26 +1294,29 @@ import { ConfirmationService } from '@abp/ng.theme.shared'; //... constructor( - private store: Store, private fb: FormBuilder, - private booksService: BooksService, - private confirmationService: ConfirmationService // <== added this line ==> + private store: Store, + private fb: FormBuilder, + private bookService: BookService, + private confirmation: ConfirmationService // <== added this line ==> ) { } ``` * We imported `ConfirmationService`. * We injected `ConfirmationService` to the 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 : ```js -import { GetBooks, CreateUpdateBook, DeleteBook } from '../../store/actions'; //<== added DeleteBook ==> +import { GetBooks, CreateUpdateBook, DeleteBook } from '../state/books.actions' ;// <== imported DeleteBook ==> -import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; //<== added Confirmation ==> +import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; //<== imported Confirmation ==> //... -delete(id: string, name: string) { - this.confirmationService +delete(id: string) { + this.confirmation .warn('::AreYouSureToDelete', 'AbpAccount::AreYouSure') .subscribe(status => { if (status === Confirmation.Status.confirm) { @@ -1439,10 +1326,30 @@ delete(id: string, name: string) { } ``` + 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: ![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png) + +#### Add a delete button + + +Open `book-list.component.html` in `app\books\book-list` folder and modify the `ngbDropdownMenu` to add the delete button as shown below: + +```html +
+ + +
+``` + +The final actions dropdown UI looks like below: + +![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png) + {{end}} ### Next Part diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 4cbefde69e..14822c9fe2 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -96,9 +96,9 @@ namespace Acme.BookStore ```` * `IRepository` is injected and used it in the `SeedAsync` to create two book entities as the test data. +* `IGuidGenerator` is injected to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases. Further information, see the [Guid generation document](../Guid-Generation.md). -### Testing the application service BookAppService -* `IGuidGenerator` is injected to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases. Further information, see the [Guid generation document](https://docs.abp.io/{{Document_Language_Code}}/abp/{{Document_Version}}/Guid-Generation). +### Testing the application service BookAppService Create a test class named `BookAppService_Tests` in the `Acme.BookStore.Application.Tests` project: diff --git a/docs/en/Tutorials/images/bookstore-angular-file-tree.png b/docs/en/Tutorials/images/bookstore-angular-file-tree.png index 28e570f604..a3197b6457 100644 Binary files a/docs/en/Tutorials/images/bookstore-angular-file-tree.png and b/docs/en/Tutorials/images/bookstore-angular-file-tree.png differ diff --git a/docs/en/Tutorials/images/generate-proxy-command.png b/docs/en/Tutorials/images/generate-proxy-command.png new file mode 100644 index 0000000000..f850ce07a2 Binary files /dev/null and b/docs/en/Tutorials/images/generate-proxy-command.png differ diff --git a/docs/en/Tutorials/images/generated-proxies.png b/docs/en/Tutorials/images/generated-proxies.png new file mode 100644 index 0000000000..9e466e7d55 Binary files /dev/null and b/docs/en/Tutorials/images/generated-proxies.png differ diff --git a/docs/en/UI/Angular/Config-State.md b/docs/en/UI/Angular/Config-State.md index 7fc202982c..71a5da01f0 100644 --- a/docs/en/UI/Angular/Config-State.md +++ b/docs/en/UI/Angular/Config-State.md @@ -236,7 +236,7 @@ const newRoute: ABP.Route = { path: "page", invisible: false, order: 2, - requiredPolicy: "MyProjectName::MyNewPage" + requiredPolicy: "MyProjectName.MyNewPage" }; this.config.dispatchAddRoute(newRoute); @@ -248,23 +248,25 @@ The `newRoute` will be placed as at root level, i.e. without any parent routes a If you want **to add a child route, you can do this:** ```js +import { eIdentityRouteNames } from '@abp/ng.identity'; + // this.config is instance of ConfigStateService const newRoute: ABP.Route = { - parentName: "AbpAccount::Login", + parentName: eIdentityRouteNames.IdentityManagement, name: "My New Page", iconClass: "fa fa-dashboard", path: "page", invisible: false, order: 2, - requiredPolicy: "MyProjectName::MyNewPage" + requiredPolicy: "MyProjectName.MyNewPage" }; this.config.dispatchAddRoute(newRoute); // returns a state stream which emits after dispatch action is complete ``` -The `newRoute` will then be placed as a child of the parent route named `'AbpAccount::Login'` and its url will be set as `'/account/login/page'`. +The `newRoute` will then be placed as a child of the parent route named `eIdentityRouteNames.IdentityManagement` and its url will be set as `'/identity/page'`. #### Route Configuration Properties @@ -291,4 +293,4 @@ Please refer to `Config.Environment` type for all the properties you can pass to ## What's Next? -* [Component Replacement](./Component-Replacement.md) \ No newline at end of file +- [Modifying the Menu](./Modifying-the-Menu.md) \ No newline at end of file diff --git a/docs/en/UI/Angular/Confirmation-Service.md b/docs/en/UI/Angular/Confirmation-Service.md new file mode 100644 index 0000000000..2d1c5c1843 --- /dev/null +++ b/docs/en/UI/Angular/Confirmation-Service.md @@ -0,0 +1,185 @@ +# Confirmation Popup + +You can use the `ConfirmationService` in @abp/ng.theme.shared package to display a confirmation popup by placing at the root level in your project. + + +## Getting Started + +You do not have to provide the `ConfirmationService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components, directives, or services. + + +```js +import { ConfirmationService } from '@abp/ng.theme.shared'; + +@Component({ + /* class metadata here */ +}) +class DemoComponent { + constructor(private confirmation: ConfirmationService) {} +} +``` + +## Usage + +You can use the `success`, `warn`, `error`, and `info` methods of `ConfirmationService` to display a confirmation popup. + +### How to Display a Confirmation Popup + +```js +const confirmationStatus$ = this.confirmation.success('Message', 'Title'); +``` + +- The `ConfirmationService` methods accept three parameters that are `message`, `title`, and `options`. +- `success`, `warn`, `error`, and `info` methods return an [RxJS Subject](https://rxjs-dev.firebaseapp.com/guide/subject) to listen to confirmation popup closing event. The type of event value is [`Confirmation.Status`](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts#L24) that is an enum. + +### How to Listen Closing Event + +You can subscribe to the confirmation closing event like below: + +```js +import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared'; + +constructor(private confirmation: ConfirmationService) {} + +this.confirmation + .warn('::WillBeDeleted', { key: '::AreYouSure', defaultValue: 'Are you sure?' }) + .subscribe((status: Confirmation.Status) => { + // your code here + }); +``` + + +- The `message` and `title` parameters accept a string, localization key or localization object. See the [localization document](./Localization.md) +- `Confirmation.Status` is an enum and has three properties; + - `Confirmation.Status.confirm` is a closing event value that will be emitted when the popup is closed by the confirm button. + - `Confirmation.Status.reject` is a closing event value that will be emitted when the popup is closed by the cancel button. + - `Confirmation.Status.dismiss` is a closing event value that will be emitted when the popup is closed by pressing the escape. + + +If you are not interested in the confirmation status, you do not have to subscribe to the returned observable: + +```js +this.confirmation.error('You are not authorized.', 'Error'); +``` + +### How to Display a Confirmation Popup With Given Options + +Options can be passed as the third parameter to `success`, `warn`, `error`, and `info` methods: + +```js +const options: Partial = { + hideCancelBtn: false, + hideYesBtn: false, + cancelText: 'Close', + yesText: 'Confirm', + messageLocalizationParams: ['Demo'], + titleLocalizationParams: [], +}; + +this.confirmation.warn( + 'AbpIdentity::RoleDeletionConfirmationMessage', + 'Are you sure?', + options, +); +``` + +- `hideCancelBtn` option hides the cancellation button when `true`. Default value is `false` +- `hideYesBtn` option hides the confirmation button when `true`. Default value is `false` +- `cancelText` is the text of the cancellation button. A localization key or localization object can be passed. Default value is `AbpUi::Cancel` +- `yesText` is the text of the confirmation button. A localization key or localization object can be passed. Default value is `AbpUi::Yes` +- `messageLocalizationParams` is the interpolation parameters for the localization of the message. +- `titleLocalizationParams` is the interpolation parameters for the localization of the title. + +With the options above, the confirmation popup looks like this: + +![confirmation](./images/confirmation.png) + +You are able to pass in an HTML string as title, message, or button texts. Here is an example: + +```js +const options: Partial = { + yesText: 'Yes, delete it', +}; + +this.confirmation.warn( + ` + Role Demo will be deleted +
+ Do you confirm that? + `, + 'Are you sure?', + options, +); +``` + +Since the values are HTML now, localization should be handled manually. Check out the [LocalizationService](./Localization#using-the-localization-service) to see how you can accomplish that. + +> Please note that all strings will be sanitized by Angular and not every HTML string will work. Only values that are considered as "safe" by Angular will be displayed. + +### How to Remove a Confirmation Popup + +The open confirmation popup can be removed manually via the `clear` method: + +```js +this.confirmation.clear(); +``` + +## API + +### success + +```js +success( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +> See the [`Config.LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46) and [`Confirmation` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts) + + +### warn + +```js +warn( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### error + +```js +error( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### info + +```js +info( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### clear + +```js +clear( + status: Confirmation.Status = Confirmation.Status.dismiss +): void +``` + +- `status` parameter is the value of the confirmation closing event. + + +## What's Next? + +- [Toast Overlay](./Toaster-Service.md) diff --git a/docs/en/UI/Angular/Http-Requests.md b/docs/en/UI/Angular/HTTP-Requests.md similarity index 99% rename from docs/en/UI/Angular/Http-Requests.md rename to docs/en/UI/Angular/HTTP-Requests.md index 71878c8045..2ac5aa1b49 100644 --- a/docs/en/UI/Angular/Http-Requests.md +++ b/docs/en/UI/Angular/HTTP-Requests.md @@ -206,4 +206,4 @@ You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob ## What's Next? -* [Localization](./Localization.md) \ No newline at end of file +* [Localization](./Localization.md) diff --git a/docs/en/UI/Angular/Modifying-the-Menu.md b/docs/en/UI/Angular/Modifying-the-Menu.md new file mode 100644 index 0000000000..450a5aecd1 --- /dev/null +++ b/docs/en/UI/Angular/Modifying-the-Menu.md @@ -0,0 +1,199 @@ +# Modifying the Menu + +The menu is inside the `ApplicationLayoutComponent` in the @abp/ng.theme.basic package. There are several methods for modifying the menu elements. This document covers these methods. If you would like to replace the menu completely, please refer to [Component Replacement documentation](./Component-Replacement.md) and learn how to replace a layout. + + + + + +## How to Add a Logo + +The `logoUrl` property in the environment variables is the url of the logo. + +You can add your logo to `src/assets` folder and set the `logoUrl` as shown below: + +```js +export const environment = { + // other configurations + application: { + name: 'MyProjectName', + logoUrl: 'assets/logo.png', + }, + // other configurations +}; +``` + +## How to Add a Navigation Element + +### 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 add the `routes` property like below: + +```js +{ + path: 'your-path', + data: { + routes: { + name: 'Your navigation', + order: 3, + iconClass: 'fas fa-question-circle', + requiredPolicy: 'permission key here', + children: [ + { + path: 'child', + name: 'Your child navigation', + order: 1, + 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. + +```js +// this.config is instance of ConfigStateService + +const newRoute: ABP.Route = { + name: 'My New Page', + iconClass: 'fa fa-dashboard', + path: 'page', + invisible: false, + order: 2, + requiredPolicy: 'MyProjectName.MyNewPage', +} as Omit; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +The `newRoute` will be placed as at root level, i.e. without any parent routes, and its url will be stored as `'/path'`. + +If you want **to add a child route, you can do this:** + +```js +// this.config is instance of ConfigStateService +// eIdentityRouteNames enum can be imported from @abp/ng.identity + +const newRoute: ABP.Route = { + parentName: eIdentityRouteNames.IdentityManagement, + name: 'My New Page', + iconClass: 'fa fa-dashboard', + path: 'page', + invisible: false, + order: 3, + requiredPolicy: 'MyProjectName.MyNewPage' +} as Omit; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +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 new route will be added like below: + +![navigation-menu-via-config-state](./images/navigation-menu-via-config-state.png) + +## How to Patch 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. + +```js +// this.config is instance of ConfigStateService +// eIdentityRouteNames enum can be imported from @abp/ng.identity + +const newRouteConfig: Partial = { + iconClass: 'fas fa-home', + parentName: eIdentityRouteNames.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 +``` + +* Moved the _Home_ navigation under the _Administration_ dropdown based on given `parentName`. +* Added an icon. +* Specified the order. +* Added a child route named _Dashboard_. + +After the patch above, navigation elements looks like below: + +![navigation-menu-after-patching](./images/navigation-menu-after-patching.png) + + +## How to Add an Element to Right Part of the Menu + +The right part elements are stored in the `LayoutState` that is in the @abp/ng.theme.basic package. + +The `dispatchAddNavigationElement` method of the `LayoutStateService` adds an element to the right part of the menu. + +You can insert an element by adding your template to `app.component` and calling the `dispatchAddNavigationElement` method: + +```js +import { Layout, LayoutStateService } from '@abp/ng.theme.basic'; // added this line + +@Component({ + selector: 'app-root', + template: ` + + + + `, +}) +export class AppComponent { + // Added ViewChild + @ViewChild('search', { static: false, read: TemplateRef }) searchElementRef: TemplateRef; + + constructor(private layout: LayoutStateService) {} // injected LayoutStateService + + // Added ngAfterViewInit + ngAfterViewInit() { + const newElement = { + name: 'Search', + element: this.searchElementRef, + order: 1, + } as Layout.NavigationElement; + + this.layout.dispatchAddNavigationElement(newElement); + } +} +``` + +This inserts a search input to the menu. The final UI looks like below: + +![navigation-menu-search-input](./images/navigation-menu-search-input.png) + +## How to Remove an Element From Right Part of the Menu + +TODO + + +## What's Next + +* [Component Replacement](./Component-Replacement.md) diff --git a/docs/en/UI/Angular/Permission-Management.md b/docs/en/UI/Angular/Permission-Management.md index ababd25e7f..d86e8c96b2 100644 --- a/docs/en/UI/Angular/Permission-Management.md +++ b/docs/en/UI/Angular/Permission-Management.md @@ -76,4 +76,4 @@ Granted Policies are stored in the `auth` property of `ConfigState`. ## What's Next? -* [Config State](./Config-State.md) \ No newline at end of file +- [Confirmation Popup](./Confirmation-Service.md) \ No newline at end of file diff --git a/docs/en/UI/Angular/Toaster-Service.md b/docs/en/UI/Angular/Toaster-Service.md new file mode 100644 index 0000000000..b194811d9a --- /dev/null +++ b/docs/en/UI/Angular/Toaster-Service.md @@ -0,0 +1,158 @@ +# Toast Overlay + +You can use the `ToasterService` in @abp/ng.theme.shared package to display messages in an overlay by placing at the root level in your project. + + +## Getting Started + +You do not have to provide the `ToasterService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components, directives, or services. + + +```js +import { ToasterService } from '@abp/ng.theme.shared'; + +@Component({ + /* class metadata here */ +}) +class DemoComponent { + constructor(private toaster: ToasterService) {} +} +``` + +## Usage + +You can use the `success`, `warn`, `error`, and `info` methods of `ToasterService` to display an overlay. + +### How to Display a Toast Overlay + +```js +this.toast.success('Message', 'Title'); +``` + +- The `ToasterService` methods accept three parameters that are `message`, `title`, and `options`. +- `success`, `warn`, `error`, and `info` methods return the id of opened toast overlay. The toast can be removed with this id. + +### How to Display a Toast Overlay With Given Options + +Options can be passed as the third parameter to `success`, `warn`, `error`, and `info` methods: + +```js +import { Toaster, ToasterService } from '@abp/ng.theme.shared'; +//... + +constructor(private toaster: ToasterService) {} + +//... +const options: Partial = { + life: 10000, + sticky: false, + closable: true, + tapToDismiss: true, + messageLocalizationParams: ['Demo', '1'], + titleLocalizationParams: [] + }; + + this.toaster.error('AbpUi::EntityNotFoundErrorMessage', 'AbpUi::Error', options); +``` + +- `life` option is the closing time in milliseconds. Default value is `5000`. +- `sticky` option keeps toast overlay on the screen by ignoring the `life` option when `true`. Default value is `false`. +- `closable` option displays the close icon on the toast overlay when it is `true`. Default value is `true`. +- `tapToDismiss` option, when `true`, allows closing the toast overlay by clicking over it. Default value is `false`. +- `yesText` is the text of the confirmation button. A localization key or localization object can be passed. Default value is `AbpUi::Yes`. +- `messageLocalizationParams` is the interpolation parameters for the localization of the message. +- `titleLocalizationParams` is the interpolation parameters for the localization of the title. + +With the options above, the toast overlay looks like this: + +![toast](./images/toast.png) + +### How to Remove a Toast Overlay + +The open toast overlay can be removed manually via the `remove` method by passing the `id` of toast: + +```js +const toastId = this.toast.success('Message', 'Title') + +this.toast.remove(toastId); +``` + +### How to Remove All Toasts + +The all open toasts can be removed manually via the `clear` method: + +```js +this.toast.clear(); +``` + +## API + +### success + +```js +success( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +- `Config` namespace can be imported from `@abp/ng.core`. +- `Toaster` namespace can be imported from `@abp/ng.theme.shared`. + +> See the [`Config.LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46) and [`Toaster` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts) + + +### warn + +```js +warn( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### error + +```js +error( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### info + +```js +info( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### remove + +```js +remove(id: number): void +``` + +Removes an open toast by the given id. + +### clear + +```js +clear(): void +``` + +Removes all open toasts. + +## See Also + +- [Confirmation Popup](./Confirmation-Service.md) + +## What's Next? + +- [Config State](./Config-State.md) diff --git a/docs/en/UI/Angular/images/confirmation.png b/docs/en/UI/Angular/images/confirmation.png new file mode 100644 index 0000000000..efe4c98ea7 Binary files /dev/null and b/docs/en/UI/Angular/images/confirmation.png differ diff --git a/docs/en/UI/Angular/images/navigation-menu-after-patching.png b/docs/en/UI/Angular/images/navigation-menu-after-patching.png new file mode 100644 index 0000000000..2f6bf08c88 Binary files /dev/null and b/docs/en/UI/Angular/images/navigation-menu-after-patching.png differ diff --git a/docs/en/UI/Angular/images/navigation-menu-search-input.png b/docs/en/UI/Angular/images/navigation-menu-search-input.png new file mode 100644 index 0000000000..ebdc05e3e0 Binary files /dev/null and b/docs/en/UI/Angular/images/navigation-menu-search-input.png differ diff --git a/docs/en/UI/Angular/images/navigation-menu-via-app-routing.png b/docs/en/UI/Angular/images/navigation-menu-via-app-routing.png new file mode 100644 index 0000000000..4d5f61301c Binary files /dev/null and b/docs/en/UI/Angular/images/navigation-menu-via-app-routing.png differ diff --git a/docs/en/UI/Angular/images/navigation-menu-via-config-state.png b/docs/en/UI/Angular/images/navigation-menu-via-config-state.png new file mode 100644 index 0000000000..19944f154d Binary files /dev/null and b/docs/en/UI/Angular/images/navigation-menu-via-config-state.png differ diff --git a/docs/en/UI/Angular/images/toast.png b/docs/en/UI/Angular/images/toast.png new file mode 100644 index 0000000000..24cdd0fe0c Binary files /dev/null and b/docs/en/UI/Angular/images/toast.png differ diff --git a/docs/en/UI/Common/Utils/Linked-List.md b/docs/en/UI/Common/Utils/Linked-List.md index 0d6b259603..0c047d28e5 100644 --- a/docs/en/UI/Common/Utils/Linked-List.md +++ b/docs/en/UI/Common/Utils/Linked-List.md @@ -1270,7 +1270,7 @@ find(predicate: ListIteratorFn): ListNode | undefined Finds the first node from the list that matches the given predicate: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1294,7 +1294,7 @@ findIndex(predicate: ListIteratorFn): number Finds the position of the first node from the list that matches the given predicate: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1322,7 +1322,7 @@ get(position: number): ListNode | undefined Finds and returns the node with specific position in the list: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1346,7 +1346,7 @@ indexOf(value: T, compareFn?: ListComparisonFn): number Finds the position of the first node from the list that has the given value: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1368,7 +1368,7 @@ i3 === -1 You may pass a custom compare function to detect the searched value: ```js -list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); +list.addManyTail([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -1408,7 +1408,7 @@ forEach(iteratorFn: ListIteratorFn): void Runs a function on all nodes in a linked list from head to tail: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1425,7 +1425,7 @@ list.forEach((node, index) => console.log(node.value + index)); A linked list is iterable. In other words, you may use methods like `for...of` on it. ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1449,7 +1449,7 @@ toArray(): T[] Converts a linked list to an array of values: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1471,7 +1471,7 @@ toNodeArray(): ListNode[] Converts a linked list to an array of nodes: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1495,7 +1495,7 @@ toString(mapperFn: ListMapperFn = JSON.stringify): string Converts a linked list to a string representation of nodes and their relations: ```js -list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); +list.addManyTail(['a', 2, 'c', { k: 4, v: 'd' }]); // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} @@ -1562,7 +1562,7 @@ export class ListNode { - `previous` refers to the previous node in the list. ```js -list.addTailMany([ 0, 1, 2 ]); +list.addManyTail([ 0, 1, 2 ]); console.log( list.head.value, // 0 diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 8b5d41aa59..c0515c18de 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -221,8 +221,11 @@ }, { "text": "Domain Driven Design", - "path": "Domain-Driven-Design.md", "items": [ + { + "text": "Overall", + "path": "Domain-Driven-Design.md" + }, { "text": "Domain Layer", "items": [ @@ -337,10 +340,22 @@ "text": "Permission Management", "path": "UI/Angular/Permission-Management.md" }, + { + "text": "Confirmation Popup", + "path": "UI/Angular/Confirmation-Service.md" + }, + { + "text": "Toast Overlay", + "path": "UI/Angular/Toaster-Service.md" + }, { "text": "Config State", "path": "UI/Angular/Config-State.md" }, + { + "text": "Modifying the Menu", + "path": "UI/Angular/Modifying-the-Menu.md" + }, { "text": "Component Replacement", "path": "UI/Angular/Component-Replacement.md" @@ -385,8 +400,11 @@ }, { "text": "Data Access", - "path": "Data-Access.md", "items": [ + { + "text": "Overall", + "path": "Data-Access.md" + }, { "text": "Connection Strings", "path": "Connection-Strings.md" @@ -469,8 +487,11 @@ }, { "text": "Startup Templates", - "path": "Startup-Templates/Index.md", "items": [ + { + "text": "Overall", + "path": "Startup-Templates/Index.md" + }, { "text": "Application", "path": "Startup-Templates/Application.md" diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 4d037ef2fd..658545c4fd 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -135,6 +135,8 @@ abp update [options] * `--include-previews` 或 `-p`: 将预览版, 测试版本 和 rc 包 同时更新到最新版本. * `--npm`: 仅更新NPM包 * `--nuget`: 仅更新的NuGet包 +* `--solution-path` 或 `-sp`: 指定解决方案路径/目录. 默认使用当前目录 +* `--solution-name` 或 `-sn`: 指定解决方案名称. 默认在目录中搜索`*.sln`文件. ### 切换到每晚构建(预览)包 @@ -164,6 +166,10 @@ CLI的一些功能需要登录到abp.io平台. 使用你的用户名登录 abp login ``` +```bash +abp login -p +``` + 请注意,新的登录将终止先前的会话并创建一个新的会话. ### logout diff --git a/docs/zh-Hans/Getting-Started-Angular-Template.md b/docs/zh-Hans/Getting-Started-Angular-Template.md index d2ec83293b..d242b4f39b 100644 --- a/docs/zh-Hans/Getting-Started-Angular-Template.md +++ b/docs/zh-Hans/Getting-Started-Angular-Template.md @@ -1,3 +1,8 @@ -## Getting Started With the Angular Application Template +# 启动模板入门 -TODO... \ No newline at end of file +请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架: + +* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No) +* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No) + + \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md index 46348ecbdf..9d195562fd 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md @@ -2,7 +2,7 @@ 本教程将介绍如何开始以最少的依赖关系开始使用ABP开发. -通常情况下你需要下载一个 ***[启动模板](https://abp.io/Templates)*** +通常情况下你需要下载一个 ***[启动模板](Getting-Started-AspNetCore-MVC-Template.md)*** ### 创建一个新项目 diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md index 2778f3db42..d242b4f39b 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md @@ -1,102 +1,8 @@ -## ASP.NET Core MVC 模板入门 +# 启动模板入门 -### 创建新项目 +请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架: -本教程使用 **ABP CLI** 创建一个新项目. 更多选项, 请参阅[入门](https://abp.io/get-started)页面. +* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No) +* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No) -如果你之前未安装,请使用命令行安装ABP CLI: - -````bash -dotnet tool install -g Volo.Abp.Cli -```` - -在空文件夹中使用 `abp new` 命令来创建项目: - -````bash -abp new Acme.BookStore -```` - -> 你可以使用不同级别的命名空间; 例如BookStore, Acme.BookStore或Acme.Retail.BookStore. - -`new` 命令创建**分层MVC应用程序**, **Entity Framework Core**作为数据库提供程序. 但是,它还有其他选择. 有关所有可用选项,请参见[CLI文档](CLI.md) - -#### 预先要求 - -创建项目的要求: - -* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) -* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/) -* [Node v12+](https://nodejs.org) -* [Yarn v1.19+](https://classic.yarnpkg.com/) - -### 解决方案结构 - -在**Visual Studio**中打开解决方案: - -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png) - -该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目,可与**EF Core**和**SQLite**数据库内存一起使用. - -> 请参阅[应用程序模板文档](Startup-Templates/Application.md)以详细了解解决方案结构. - -### 数据库连接字符串 - -查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**: - -````json -{ - "ConnectionStrings": { - "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" - } -} -```` - -解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/zh-cn/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串. - -### 创建数据库并应用数据库迁移 - -你有两个选项来创建数据库. - -#### 使用DbMigrator应用程序 - -该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用. - -> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串. - -右键单击`.DbMigrator`项目并选择 **设置为启动项目**: - -![set-as-startup-project](images/set-as-startup-project.png) - -按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出: - -![set-as-startup-project](images/db-migrator-app.png) - -#### 使用EF Core Update-Database命令 - -Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**: - -![set-as-startup-project](images/set-as-startup-project.png) - -打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令: - -![pcm-update-database](images/pcm-update-database-v2.png) - -这将基于配置的连接字符串创建新数据库. - -> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序. - -### 运行应用程序 - -你现在可以运行应用程序,它将会打开**home**页面: - -![bookstore-homepage](images/bookstore-homepage.png) - -单击 **登录** 按钮, 输入用户名`admin`, 密码`1q2w3E*`, 登录应用程序. - -启动模板包括**身份管理**和**租户管理**模块. 登录后,将显示"管理"菜单, 你可以在其中管理**租户**,**角色**,**用户**和**权限**. 用户管理页面如下所示: - -![bookstore-user-management](images/bookstore-user-management-v2.png) - -### 下一步是什么? - -* [应用程序开发教程](Tutorials/AspNetCore-Mvc/Part-I.md) + \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started-With-Startup-Templates.md b/docs/zh-Hans/Getting-Started-With-Startup-Templates.md index 2b23350612..d242b4f39b 100644 --- a/docs/zh-Hans/Getting-Started-With-Startup-Templates.md +++ b/docs/zh-Hans/Getting-Started-With-Startup-Templates.md @@ -1,6 +1,8 @@ # 启动模板入门 -参阅下面的教程来学习如何开始使用的ABP框架预构建的应用程序启动模板: +请参阅以下教程,了解如何使用预构建的应用程序启动模板开始使用ABP框架: -* [ASP.NET Core MVC/Razor页面模板入门](Getting-Started-AspNetCore-MVC-Template.md) -* [Angular UI模板入门](Getting-Started-Angular-Template.md) \ No newline at end of file +* [ASP.NET Core MVC / Razor Pages UI 入门](Getting-Started?UI=MVC&DB=EF&Tiered=No) +* [Angular UI 入门](Getting-Started?UI=NG&DB=EF&Tiered=No) + + \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started.md b/docs/zh-Hans/Getting-Started.md new file mode 100644 index 0000000000..a2b3013fe5 --- /dev/null +++ b/docs/zh-Hans/Getting-Started.md @@ -0,0 +1,411 @@ +## 入门 + +````json +//[doc-params] +{ + "UI": ["MVC","NG"], + "DB": ["EF", "Mongo"], + "Tiered": ["Yes", "No"] +} +```` + +本教程介绍了如何创建一个新的{{if UI == "MVC"}} ASP.NET Core MVC web {{else if UI == "NG"}} Angular {{end}}. 配置并运行它. + +## 设置你的开发环境 + +创建第一个项目之前,需要正确的设置你的开发环境. + +### 预先要求 + +你需要安装以下工具: + +* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). +* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/) + +* [Node v12+](https://nodejs.org) +* [Yarn v1.19+](https://classic.yarnpkg.com/) + +> 你可以也使用其他支持.NET Core 和 ASP.NET Core的编辑器. + +### 安装ABP CLI + +[ABP CLI](./CLI.md)是一个命令行页面,用于为基于ABP的应用程序验证和自动化一些任务. + +> ABP CLI是[ABP框架](https://abp.io/)一个免费开源的工具. + +你需要使用以下命令安排ABP CLI: + +````shell +dotnet tool install -g Volo.Abp.Cli +```` + +如果你已经安装,你可以使用以下命令更新到最新版本: + +````shell +dotnet tool update -g Volo.Abp.Cli +```` + +## 创建新项目 + +> 本文假设你使用 **{{ UI_Value }}** 做为UI框架 **{{ DB_Value }}** 做为数据库提供程序,对于其它选项,你可以更改文档顶部的首选项. + +### 使用ABP CLI创建一个新项目 + +使用ABP CLI的 `new` 命令创建新项目: + +````shell +abp new Acme.BookStore -t app{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} +```` + +* `-t` 参数指定 [启动模板](Startup-Templates/Application.md) 名称. `app` 是一个启动模板名称,包含了预安装并且配置好的[ABP模块](Modules/Index.md). + +{{ if UI == "NG" }} + +* `-u` 指定UI框架, 本例中是 `angular`. + +{{ if Tiered == "Yes" }} + +* `--separate-identity-server` 参数用于将Identity服务器应用程序与API主机应用程序分隔开. 如果未指定,你将只有一个端点. + +{{ end }} + +{{ end }} + +{{ if DB == "Mongo" }} + +* `-d` 指定数据库提供程序, 本例中是 `mongodb`. + +{{ end }} + +{{ if Tiered == "Yes" && UI != "NG" }} + +* `--tiered` 参数用于创建n层解决方案,其中身份验证服务器层,UI层和API层在物理上是分离的. + +{{ end }} + +> 你可以使用不同级别的命令空间; 例如. BookStore, Acme.BookStore or Acme.Retail.BookStore. + +## 解决方案结构 + +{{ if UI == "MVC" }} + +创建项目后你会有以下解决方案目录和文件: + +![](images/solution-files-mvc.png) + +在Visual Studio中打开 `.sln` 文件时,将看到以下解决方案结构: + +{{if DB == "Mongo"}} + +![vs-default-app-solution-structure](images/vs-app-solution-structure-mongodb.png) + +{{else}} + +![vs-default-app-solution-structure](images/vs-app-solution-structure{{if Tiered == "Yes"}}-tiered{{end}}.png) + +{{end}} + +{{ else if UI == "NG" }} +在创建的解决方案中有三个文件夹: + +![](images/solution-files-non-mvc.png) + +* `angular` 文件夹包含Angular UI应用程序. +* `aspnet-core` 文件夹包含后端应用程序. +* `react-native` 文件夹包含React Native UI 应用程序. + +打开 `aspnet-core` 文件夹下的 `.sln`(`Visual Studio`解决方案)文件: +![vs-angular-app-backend-solution-structure](images/vs-spa-app-backend-structure{{if DB == "Mongo"}}-mongodb{{end}}.png) + +{{ end }} + +> ###### 关于解决方案中的项目 +> +> 根据你的**UI**,**数据库**和其他选项,你的解决方案的结构可能略有不同. + +该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目. + +{{ if DB == "EF" }} + +集成测试项目已配置为可与 **EF Core** & **SQLite 内存** database同时使用. + +{{ else if DB == "Mongo" }} + +集成测试项目已配置为每个测试创建的内存中的**MongoDB**数据库(使用的[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)库). + +{{ end }} + +> 请参阅[应用程序模板文档](Startup-Templates/Application.md)详细了解解决方案结构. + +## 创建数据库 + +### 数据库连接字符串 + +检查 {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` 和 `.HttpApi.Host` 项目{{else}}`.Web` 项目{{end}}{{else if UI == "NG" }}`.HttpApi.Host` 项目{{end}}下 `appsettings.json` 文件中的 **链接字符串**: + +{{ if DB == "EF" }} + +````json +"ConnectionStrings": { + "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" +} +```` + +该解决方案配置为**Entity Framework Core**与**MS SQL Server**一起使用. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以使用任何受支持的DBMS. 请参阅[Entity Framework集成文档](https://docs.abp.io/en/abp/latest/Entity-Framework-Core)了解如何切换到另一个DBMS. + +### 数据库连接字符串 + +查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**: + +````json +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" + } +} +```` + +解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/zh-cn/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串. + +### 应用迁移 + +该解决方案使用[Entity Framework Core Code First 迁移](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli). 你需要应用迁移来创建数据库,有两种方法迁移数据库. + +#### 使用DbMigrator应用程序应用迁移 + +该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用. + +> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串. + +右键单击`.DbMigrator`项目并选择 **设置为启动项目**: + +![set-as-startup-project](images/set-as-startup-project.png) + +按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出: + +![set-as-startup-project](images/db-migrator-app.png) + +#### 使用EF Core Update-Database命令 + +Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**: + +{{ if UI == "MVC" }} + +右键单击{{if Tiered == "Yes"}}`.IdentityServer`{{else}}`.Web`{{end}}项目并选择**设置为启动项目**: + +{{ else if UI != "MVC" }} + +右键单击`.HttpApi.Host`项目并选择**设置为启动项目**: + +{{ end }} + +![set-as-startup-project](images/set-as-startup-project.png) + +打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令: + +![package-manager-console-update-database](images/package-manager-console-update-database.png) + +这将基于配置的连接字符串创建新数据库. + +> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序. + +{{ else if DB == "Mongo" }} + +````json +"ConnectionStrings": { + "Default": "mongodb://localhost:27017/BookStore" +} +```` + +该解决方案被配置为在你的本地计算机中使用 **MongoDB**,因此你需要启动并运行一个MongoDB服务器实例或者将连接字符串更改为另一个MongoDB服务器. + +### 初始化种子数据 + +该解决方案附带一个 `.DbMigrator` 控制台应用程序,该应用程序为初始数据提供了种子. 它对于开发以及生产环境都很有用. + +> `.DbMigrator` 项目有自己的 `appsettings.json`.如果你更改了其他项目的 `appsettings.json`,也应该更改这个. + +右键点击 `.DbMigrator` 并选择 **设置为启动项目**. + +![set-as-startup-project](images/set-as-startup-project.png) + + 按F5(或Ctrl+F5)启动应用程序,你会看到以下输出: + + ![db-migrator-output](images/db-migrator-output.png) + +> 数据库创建后会初始化种子数据, 其中包含用于登录的 `admin` 用户. 所以你至少使用 `.DbMigrator` 一次. + +{{ end }} + +### 运行应用程序 + +{{ if UI == "MVC" }} + +{{ if Tiered == "Yes" }} + +确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. + +> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. + +你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器. + +确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面. + +![swagger-ui](images/swagger-ui.png) + +这里是Web应用程序使用的API应用程序. + +最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **welcome** 页面. + +![mvc-tiered-app-home](images/bookstore-home.png) + +点击 **login** 按钮重定向到 `Identity Server` 来登录应用程序. + +![bookstore-login](images/bookstore-login.png) + +{{ else }} + +最后确保 `.Web` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. + +> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. + +![bookstore-login](images/bookstore-login.png) + +{{ end }} + +{{ else if UI != "MVC" }} + +#### 运行HTTP API Host (服务器端) + +{{ if Tiered == "Yes" }} + +确保 `.IdentityServer` 是启动项目,运行应用程序后会在你的浏览器打开一个 **login** 页面. + +> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. + +你可以登录,但是不能在这里进入主应用程序,它仅是验证服务器. + +{{ end }} + +确保 `.HttpApi.Host` 是启动项目,运行应用程序后会在你的浏览器打开一个 **Swagger UI** 页面. + +{{ if Tiered == "No" }} + +> 在Visual Studio中使用Ctrl+F5(而不是F5)运行应用,如果你不用于调试,这会减少启动时间. + +{{ end }} + +![swagger-ui](images/swagger-ui.png) + +你可以看到应用程序的API并进行测试. 更多信息,请参阅[Swagger UI](https://swagger.io/tools/swagger-ui/). + +> ##### Swagger UI 授权 +> +> 大多数的HTTP API都需要身份验证和授权. 如果你要测试授权API, 请手动进入 `/Account/Login` 页面, 输入用户名: `admin` 和密码: `1q2w3E*` 登录到应用程序. 然后你可以访问授权API. + +{{ end }} + +{{ if UI == "NG" }} +#### 运行 Angular 应用程序 (客户端) + +在 `angular` 下打开命令行终端, 输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作). + +```bash +yarn +``` + +等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令: + +```bash +yarn start +``` + +等待 `Angular CLI` 使用 `BrowserSync` 启动 `Webpack` dev-server. +它会负责编译你的 `TypeScript`代码, 并自动重新加载浏览器. +完成后 `Angular Live Development Server` 会监听 localhost:4200. +打开你的浏览器并导航到[localhost:4200](http://localhost:4200/). + +![bookstore-login](images/bookstore-login.png) + +{{ end }} + +输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序. + +![bookstore-home](images/bookstore-home.png) + +应用程序已经启动并执行,你可以基于该启动模板开发应用程序. + +#### 移动开发 + +ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序. + +> 该解决方案默认 `react-native` 包含了React Native应用程序,如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹. + +运行在Android模拟器或真机上的React Native应用程序无法连接到 `localhost` 上的后.要修复此问题,需要在本地IP上运行后端. + +{{ if Tiered == "No"}} +![React Native host project local IP entry](images/rn-host-local-ip.png) + +* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 和 `Authority` 属性的 `localhost` 替换为你本地的IP地址. +* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址. + +{{ else if Tiered == "Yes" }} + +![React Native tiered project local IP entry](images/rn-tiered-local-ip.png) + +* 打开 `.IdentityServer` 文件夹下的 `appsettings.json` 文件. 将 `SelfUrl` 属性的 `localhost` 替换为你本地的IP地址. +* 打开 `.IdentityServer/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址. +* 打开 `.HttpApi.Host` 文件夹下的 `appsettings.json` 文件. 将 `Authority` 属性的 `localhost` 替换为你本地的IP地址. +* 打开 `.HttpApi.Host/Properties` 文件夹下的 `launchSettings.json` 文件. 将 `applicationUrl` 属性的 `localhost` 替换为你本地的IP地址. + +{{ end }} + +按照**运行HTTP API Host (服务端口)**那样运行后端. + +> React Native应用程序不信任自动生成的.NET HTTPS证书,你可以在开发期间使用HTTP. + +在 `react-native` 文件夹打开命令行终端,输入 `yarn` 命令(我们推荐使用[yarn](https://yarnpkg.com/)包管理, `npm install` 在大多数情况下也可以工作). + +```bash +yarn +``` + +* 打开 `react-nativer` 文件夹下的 `Environment.js` 文件. 将 `apiUrl` 和 `issuer` 属性的 `localhost` 替换为你本地的IP地址: + +![react native environment local IP](images/rn-environment-local-ip.png) + +{{ if Tiered == "Yes" }} + +> 确保 `issuer` 与正在运行的 `.IdentityServer` 项目匹配, `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配. + +{{else}} + +> 确保 `issuer` 和 `apiUrl` 与正在运行的 `.HttpApi.Host` 项目匹配 + +{{ end }} + +等到所有node模块加载成功, 执行 `yarn start` (或 `npm start`) 命令: + +```bash +yarn start +``` + +等待Expo CLI启动后Expo CLI在 `http://localhost:19002/` 地址要开管理页面. + +![expo-interface](images/rn-expo-interface.png) + +在上面的管理界面中,可以通过使用[Expo Client](https://expo.io/tools#client)扫描二维码,使用Android模拟器,iOS模拟器或真机来启动应用程序. + +> 请参阅[expo.io](https://docs.expo.io/versions/v36.0.0/workflow/ios-simulator/)上的[Android Studio模拟器](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/)和[iOS模拟器文档](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/). + +![React Native login screen on iPhone 11](images/rn-login-iphone.png) + +输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序. + +应用程序已经启动并执行,你可以基于该启动模板开发应用程序. + +> [应用程序启动模板](Startup-Templates/Application.md) 包含租户管理和Identity模块. + +## 下一步是什么? + +[应用程序开发教程](Tutorials/Part-1.md) diff --git a/docs/zh-Hans/Specifications.md b/docs/zh-Hans/Specifications.md new file mode 100644 index 0000000000..f927c80255 --- /dev/null +++ b/docs/zh-Hans/Specifications.md @@ -0,0 +1,3 @@ +## 规约 + +TODO.. \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png deleted file mode 100644 index a7d49a661b..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png deleted file mode 100644 index f531e6f457..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png deleted file mode 100644 index 7cc96c8c94..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png deleted file mode 100644 index 79314dd2dc..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png deleted file mode 100644 index dfd4b5d8aa..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png deleted file mode 100644 index edf2826361..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png deleted file mode 100644 index 437c772503..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/Part-1.md b/docs/zh-Hans/Tutorials/Part-1.md new file mode 100644 index 0000000000..43590faf2b --- /dev/null +++ b/docs/zh-Hans/Tutorials/Part-1.md @@ -0,0 +1,1078 @@ +## ASP.NET Core {{UI_Value}} 教程 - 第一章 +````json +//[doc-params] +{ + "UI": ["MVC","NG"] +} +```` +{{ +if UI == "MVC" + DB="ef" + DB_Text="Entity Framework Core" + UI_Text="mvc" +else if UI == "NG" + DB="mongodb" + DB_Text="MongoDB" + UI_Text="angular" +else + DB ="?" + UI_Text="?" +end +}} + +### 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的应用程序. **{{DB_Text}}**将用作ORM提供者,前端使用{{UI_Value}} 和 JavaScript. + +ASP.NET Core {{UI_Value}} 系列教程包括三个3个部分: + +- **Part-1: 创建项目和书籍列表页面(本章)** +- [Part-2: 创建,编辑,删除书籍](Part-2.md) +- [Part-3: 集成测试](Part-3.md) + +> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). + +### 创建新项目 + +创建一个名为 `Acme.BookStore` 的新项目,其中 `Acme` 是公司名 `BookStore` 是项目名. 你可以参阅[入门](../Getting-Started?UI={{UI}}#run-the-application) 文档了解如何创建新项目. 我们将使用CLI创建新项目. + +#### 创建项目 + +使用以下命令创建一个新的ABP项目,使用 `{{DB_Text}}` 做为数据库提供者, UI选项使用 `{{UI_Value}}`. 其他CLI选项请参考[ABP CLI](https://docs.abp.io/en/abp/latest/CLI)文档. + +```bash +abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none +``` + +![Creating project](./images/bookstore-create-project-{{UI_Text}}.png) + +### 应用迁移 + +项目创建后,需要应用初始化迁移创建数据库. 运行 `Acme.BookStore.DbMigrator` 应用程序. 它会应用所有迁移,完成流程后你会看到以下结果,数据库已经准备好了! + +![Migrations applied](./images/bookstore-migrations-applied-{{UI_Text}}.png) + +> 另外你也可以在 Visual Studio 包管理控制台运行 `Update-Database` 命令应用迁移. + +#### 初始化数据库表 + +![Initial database tables](./images/bookstore-database-tables-{{DB}}.png) + +### 运行应用程序 + +右键单击{{if UI == "MVC"}} `Acme.BookStore.Web`{{end}} {{if UI == "NG"}} `Acme.BookStore.HttpApi.Host` {{end}} 项目**设置为启动项**. 使用 **CTRL+F5** 或 **F5** 运行应用程序. {{if UI == "NG"}}你会看到BookStore API的Swagger UI.{{end}} + +更多信息,参阅[入门教程](../../Getting-Started?UI={{UI}})的运行应用程序部分. + +![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png) + +{{if UI == "NG"}} + +在 `angular` 下打开命令行终端,执行 `yarn` 命令: + +```bash +yarn +``` + +所有的模块加载后,执行 `yarn start` 命令: + +```bash +yarn start +``` + +默认网站从以下URL访问: + +http://localhost:4200/ + +如果你成功看到登录页面,可以按 `ctrl-c` 退出Angular托管.(我们稍后再运行). + +> 注意, Firefox不使用Windows凭据存储,你需要手动将自签名的开发人员证书导入到Firefox. 打开Firefox并导航到以下网址: +> +> https://localhost:44322/api/abp/application-configuration +> +> 如果你看到下图,单击 **Accept the Risk 和 Continue** 按钮绕过警告. +> +> ![Set as startup project](./images/mozilla-self-signed-cert-error.png) + +{{end}} + +默认的登录凭证: + +* **Username**: admin +* **Password**: 1q2w3E* + +### 解决方案的结构 + +下面的图片展示了从启动模板创建的项目是如何分层的. + +![bookstore-visual-studio-solution](./images/bookstore-solution-structure-{{UI_Text}}.png) + +> 你可以查看[应用程序模板文档](../startup-templates/application#solution-structure)以详细了解解决方案结构. + +### 创建Book实体 + +启动模板中的域层分为两个项目: + + - `Acme.BookStore.Domain`包含你的[实体](https://docs.abp.io/zh-Hans/abp/latest/Entities), [领域服务](https://docs.abp.io/zh-Hans/abp/latest/Domain-Services)和其他核心域对象. + - `Acme.BookStore.Domain.Shared`包含可与客户共享的常量,枚举或其他域相关对象. + +在解决方案的**领域层**(`Acme.BookStore.Domain`项目)中定义[实体](https://docs.abp.io/zh-Hans/abp/latest/Entities). 该应用程序的主要实体是`Book`. 在`Acme.BookStore.Domain`项目中创建一个名为`Book`的类,如下所示: + +````C# +using System; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Acme.BookStore +{ + public class Book : AuditedAggregateRoot + { + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + + protected Book() + { + } + public Book(Guid id, string name, BookType type, DateTime publishDate, float price) + :base(id) + { + Name = name; + Type = type; + PublishDate = publishDate; + Price = price; + } + } +} +```` + +* ABP为实体提供了两个基本的基类: `AggregateRoot`和`Entity`. **Aggregate Root**是**域驱动设计(DDD)** 概念之一. 有关详细信息和最佳做法,请参阅[实体文档](https://docs.abp.io/zh-Hans/abp/latest/Entities). +* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime` 等). +* `Guid`是`Book`实体的主键类型. +* 使用 **数据注解** 为EF Core添加映射.或者你也可以使用 EF Core 自带的[fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling). + +#### BookType枚举 + +上面所用到的`BookType`枚举定义如下: + +````C# +namespace Acme.BookStore +{ + public enum BookType + { + Undefined, + Adventure, + Biography, + Dystopia, + Fantastic, + Horror, + Science, + ScienceFiction, + Poetry + } +} +```` + +#### 将Book实体添加到DbContext中 + +{{if DB == "ef"}} + +EF Core需要你将实体和 `DbContext` 建立关联.最简单的做法是在`Acme.BookStore.EntityFrameworkCore`项目的`BookStoreDbContext`类中添加`DbSet`属性.如下所示: + +````C# +public class BookStoreDbContext : AbpDbContext +{ + public DbSet Books { get; set; } + ... +} +```` + +{{end}} + +{{if DB == "mongodb"}} + +添加 `IMongoCollection Books` 属性到 `Acme.BookStore.MongoDB` 项目的 `BookStoreMongoDbContext` 中. + +```csharp +public class BookStoreMongoDbContext : AbpMongoDbContext +{ + public IMongoCollection Users => Collection(); + public IMongoCollection Books => Collection();//<--added this line--> + //... +} +``` + +{{end}} + +{{if DB == "ef"}} + +#### 配置你的Book实体 + +在 `Acme.BookStore.EntityFrameworkCore` 项目中打开 `BookStoreDbContextModelCreatingExtensions.cs` 文件,并将以下代码添加到 `ConfigureBookStore` 方法的末尾以配置Book实体: + +````csharp +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); + b.ConfigureByConvention(); //auto configure for the base class props + b.Property(x => x.Name).IsRequired().HasMaxLength(128); +}); +```` + +添加 `using Volo.Abp.EntityFrameworkCore.Modeling;` 以使用 `ConfigureByConvention` 扩展方法. + +{{end}} + +{{if DB == "mongodb"}} + +#### 添加种子数据 + +添加种子数据是可选的,但第一次运行时最好将初始数据添加到数据库中. ABP提供了[数据种子系统](https://docs.abp.io/en/abp/latest/Data-Seeding). 在 `*.Domain` 项目下创建派生 `IDataSeedContributor` 的类: + +```csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; + +namespace Acme.BookStore +{ + public class BookStoreDataSeederContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; + + public BookStoreDataSeederContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) + { + _bookRepository = bookRepository; + _guidGenerator = guidGenerator; + } + + public async Task SeedAsync(DataSeedContext context) + { + if (await _bookRepository.GetCountAsync() > 0) + { + return; + } + + await _bookRepository.InsertAsync( + new Book( + id: _guidGenerator.Create(), + name: "1984", + type: BookType.Dystopia, + publishDate: new DateTime(1949, 6, 8), + price: 19.84f + ) + ); + + await _bookRepository.InsertAsync( + new Book( + id: _guidGenerator.Create(), + name: "The Hitchhiker's Guide to the Galaxy", + type: BookType.ScienceFiction, + publishDate: new DateTime(1995, 9, 27), + price: 42.0f + ) + ); + } + } +} +``` + +{{end}} + +{{if DB == "ef"}} + +#### 添加新的Migration并更新数据库 + +这个启动模板使用了[EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/)来创建并维护数据库结构.打开 **程序包管理器控制台(Package Manager Console) (PMC)** (工具/Nuget包管理器菜单) + +![Open Package Manager Console](./images/bookstore-open-package-manager-console.png) + +选择 `Acme.BookStore.EntityFrameworkCore.DbMigrations`作为默认的项目然后执行下面的命令: + +```bash +Add-Migration "Created_Book_Entity" +``` + +![bookstore-pmc-add-book-migration](./images/bookstore-pmc-add-book-migration-v2.png) + +这样就会在 `Migrations` 文件夹中创建一个新的migration类.然后执行 `Update-Database` 命令更新数据库结构: + +````bash +Update-Database +```` + +![bookstore-update-database-after-book-entity](./images/bookstore-update-database-after-book-entity.png) + +#### 添加示例数据 + +`Update-Database`命令在数据库中创建了`AppBooks`表. 打开数据库并输入几个示例行,以便在页面上显示它们: + +```mssql +INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES +('f3c04764-6bfd-49e2-859e-3f9bfda6183e', '2018-07-01', '1984',3,'1949-06-08','19.84') + +INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES +('13024066-35c9-473c-997b-83cd8d3e29dc', '2018-07-01', 'The Hitchhiker`s Guide to the Galaxy',7,'1995-09-27','42') + +INSERT INTO AppBooks (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES +('4fa024a1-95ac-49c6-a709-6af9e4d54b54', '2018-07-02', 'Pet Sematary',5,'1983-11-14','23.7') +``` + +![bookstore-books-table](./images/bookstore-books-table.png) + +{{end}} + +### 创建应用服务 + +下一步是创建[应用服务](../Application-Services.md)来管理(创建,列出,更新,删除)书籍. 启动模板中的应用程序层分为两个项目: + +* `Acme.BookStore.Application.Contracts`主要包含你的DTO和应用程序服务接口. +* `Acme.BookStore.Application`包含应用程序服务的实现. + +#### BookDto + +在`Acme.BookStore.Application.Contracts`项目中创建一个名为`BookDto`的DTO类: + +````C# +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore +{ + public class BookDto : AuditedEntityDto + { + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + } +} +```` + +* **DTO**类被用来在 **表示层** 和 **应用层** **传递数据**.查看[DTO文档](https://docs.abp.io/zh-Hans/abp/latest/Data-Transfer-Objects)查看更多信息. +* 为了在页面上展示书籍信息,`BookDto`被用来将书籍数据传递到表示层. +* `BookDto`继承自 `AuditedEntityDto`.跟上面定义的`Book`类一样具有一些审计属性. + +在将书籍返回到表示层时,需要将`Book`实体转换为`BookDto`对象. [AutoMapper](https://automapper.org)库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在`Acme.BookStore.Application`项目的`BookStoreApplicationAutoMapperProfile`类中定义映射: + +````csharp +using AutoMapper; + +namespace Acme.BookStore +{ + public class BookStoreApplicationAutoMapperProfile : Profile + { + public BookStoreApplicationAutoMapperProfile() + { + CreateMap(); + } + } +} +```` + +#### CreateUpdateBookDto + +在`Acme.BookStore.Application.Contracts`项目中创建一个名为`CreateUpdateBookDto`的DTO类: + +````c# +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AutoMapper; + +namespace Acme.BookStore +{ + public class CreateUpdateBookDto + { + [Required] + [StringLength(128)] + public string Name { get; set; } + + [Required] + public BookType Type { get; set; } = BookType.Undefined; + + [Required] + public DateTime PublishDate { get; set; } + + [Required] + public float Price { get; set; } + } +} +```` + +* 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息. +* 它定义了数据注释属性(如`[Required]`)来定义属性的验证. DTO由ABP框架[自动验证](https://docs.abp.io/zh-Hans/abp/latest/Validation). + +就像上面的`BookDto`一样,创建一个从`CreateUpdateBookDto`对象到`Book`实体的映射: + +````csharp +using AutoMapper; + +namespace Acme.BookStore +{ + public class BookStoreApplicationAutoMapperProfile : Profile + { + public BookStoreApplicationAutoMapperProfile() + { + CreateMap(); + CreateMap(); //<--added this line--> + } + } +} +```` + +#### IBookAppService + +在`Acme.BookStore.Application.Contracts`项目中定义一个名为`IBookAppService`的接口: + +````C# +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore +{ + public interface IBookAppService : + ICrudAppService< //定义了CRUD方法 + BookDto, //用来展示书籍 + Guid, //Book实体的主键 + PagedAndSortedResultRequestDto, //获取书籍的时候用于分页和排序 + CreateUpdateBookDto, //用于创建书籍 + CreateUpdateBookDto> //用于更新书籍 + { + + } +} +```` + +* 框架定义应用程序服务的接口**不是必需的**. 但是,它被建议作为最佳实践. +* `ICrudAppService`定义了常见的**CRUD**方法:`GetAsync`,`GetListAsync`,`CreateAsync`,`UpdateAsync`和`DeleteAsync`. 你可以从空的`IApplicationService`接口继承并手动定义自己的方法. +* `ICrudAppService`有一些变体, 你可以在每个方法中使用单独的DTO,也可以分别单独指定. + +#### BookAppService + +在`Acme.BookStore.Application`项目中实现名为`BookAppService`的`IBookAppService`: + +````C# +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore +{ + public class BookAppService : + CrudAppService, + IBookAppService + { + public BookAppService(IRepository repository) + : base(repository) + { + + } + } +} +```` + +* `BookAppService`继承了`CrudAppService<...>`.它实现了上面定义的CRUD方法. +* `BookAppService`注入`IRepository `,这是`Book`实体的默认仓储. ABP自动为每个聚合根(或实体)创建默认仓储. 请参阅[仓储文档](https://docs.abp.io/zh-Hans/abp/latest/Repositories) +* `BookAppService`使用`IObjectMapper`将`Book`对象转换为`BookDto`对象, 将`CreateUpdateBookDto`对象转换为`Book`对象. 启动模板使用[AutoMapper](http://automapper.org/)库作为对象映射提供程序. 你之前定义了映射, 因此它将按预期工作. + +### 自动生成API Controllers + +你通常创建**Controller**以将应用程序服务公开为**HTTP API**端点. 因此允许浏览器或第三方客户端通过AJAX调用它们. ABP可以[**自动**](https://docs.abp.io/zh-Hans/abp/latest/API/Auto-API-Controllers)按照惯例将你的应用程序服务配置为MVC API控制器. + +#### Swagger UI + +启动模板配置为使用[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)运行[swagger UI](https://swagger.io/tools/swagger-ui/). 运行应用程序并在浏览器中输入`https://localhost:XXXX/swagger/`(用你自己的端口替换XXXX)作为URL. + +你会看到一些内置的接口和`Book`的接口,它们都是REST风格的: + +![bookstore-swagger](images/bookstore-swagger.png) + +Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表. + +{{if UI == "MVC"}} + +### 动态JavaScript代理 + +在Javascript端通过AJAX的方式调用HTTP API接口是很常见的,你可以使用`$.ajax`或者其他的工具来调用接口.当然,ABP中提供了更好的方式. + +ABP **自动** 为所有的API接口创建了JavaScript **代理**.因此,你可以像调用 **JavaScript function**一样调用任何接口. + +#### 在浏览器的开发者控制台中测试接口 + +你可以使用你钟爱的浏览器的 **开发者控制台** 中轻松测试JavaScript代理.运行程序,并打开浏览器的 **开发者工具**(快捷键:F12),切换到 **Console** 标签,输入下面的代码并回车: + +````js +acme.bookStore.book.getList({}).done(function (result) { console.log(result); }); +```` + +* `acme.bookStore`是`BookAppService`的命名空间,转换成了[驼峰命名](https://en.wikipedia.org/wiki/Camel_case). +* `book`是`BookAppService`转换后的名字(去除了AppService后缀并转成了驼峰命名). +* `getList`是定义在`AsyncCrudAppService`基类中的`GetListAsync`方法转换后的名字(去除了Async后缀并转成了驼峰命名). +* `{}`参数用于将空对象发送到`GetListAsync`方法,该方法通常需要一个类型为`PagedAndSortedResultRequestDto`的对象,用于向服务器发送分页和排序选项(所有属性都是可选的,所以你可以发送一个空对象). +* `getList`方法返回了一个`promise`.因此,你可以传递一个回调函数到`done`(或者`then`)方法中来获取服务返回的结果. + +运行这段代码会产生下面的输出: + +![bookstore-test-js-proxy-getlist](./images/bookstore-test-js-proxy-getlist.png) + +你可以看到服务器返回的 **book list**.你还可以切换到开发者工具的 **network** 查看客户端到服务器端的通讯信息: + +![bookstore-test-js-proxy-getlist-network](./images/bookstore-test-js-proxy-getlist-network.png) + +我们使用`create`方法 **创建一本新书**: + +````js +acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05-24', price: 21.5 }).done(function (result) { console.log('successfully created the book with id: ' + result.id); }); +```` + +你会看到控制台会显示类似这样的输出: + +````text +successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 +```` + +检查数据库中的`Books`表以查看新书. 你可以自己尝试`get`,`update`和`delete`功能. + +### 创建书籍页面 + +现在我们来创建一些可见和可用的东西,取代经典的MVC,我们使用微软推荐的[Razor Pages UI](https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start). + +在 `Acme.BookStore.Web`项目的`Pages`文件夹下创建一个新的文件夹叫`Books`并添加一个名为`Index.cshtml`的Razor Page. + +![bookstore-add-index-page](./images/bookstore-add-index-page-v2.png) + +打开`Index.cshtml`并把内容修改成下面这样: + +**Index.cshtml:** + +````html +@page +@using Acme.BookStore.Web.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePage +@model IndexModel + +

Books

+```` + +* 此代码更改了Razor View Page Model的默认继承,因此它从`BookStorePage`类(而不是`PageModel`)继承.启动模板附带的`BookStorePage`类,提供所有页面使用的一些共享属性/方法. +* 确保`IndexModel`(Index.cshtml.cs)具有`Acme.BookStore.Web.Pages.Books`命名空间,或者在`Index.cshtml`中更新它. + +**Index.cshtml.cs:** + +```csharp +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Acme.BookStore.Web.Pages.Books +{ + public class IndexModel : PageModel + { + public void OnGet() + { + + } + } +} +``` + +#### 将Books页面添加到主菜单 + +打开`Menus`文件夹中的 `BookStoreMenuContributor` 类,在`ConfigureMainMenuAsync`方法的底部添加如下代码: + +````csharp +//... +namespace Acme.BookStore.Web.Menus +{ + public class BookStoreMenuContributor : IMenuContributor + { + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + //<-- added the below code + context.Menu.AddItem( + new ApplicationMenuItem("BooksStore", l["Menu:BookStore"]) + .AddItem( + new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books") + ) + ); + //--> + } + } +} +```` + +{{end}} + +#### 本地化菜单 + +本地化文本位于`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下: + +![bookstore-localization-files](./images/bookstore-localization-files-v2.png) + +打开`en.json`文件,将`Menu:BookStore`和`Menu:Books`键的本地化文本添加到文件末尾: + +````json +{ + "Culture": "en", + "Texts": { + "Menu:Home": "Home", + "Welcome": "Welcome", + "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.", + + "Menu:BookStore": "Book Store", + "Menu:Books": "Books", + "Actions": "Actions", + "Edit": "Edit", + "PublishDate": "Publish date", + "NewBook": "New book", + "Name": "Name", + "Type": "Type", + "Price": "Price", + "CreationTime": "Creation time", + "AreYouSureToDelete": "Are you sure you want to delete this item?" + } +} +```` + +* ABP的本地化功能建立在[ASP.NET Core's standard localization]((https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization))之上并增加了一些扩展.查看[本地化文档](https://docs.abp.io/zh-Hans/abp/latest/Localization). +* 本地化key是任意的. 你可以设置任何名称. 我们更喜欢为菜单项添加`Menu:`前缀以区别于其他文本. 如果未在本地化文件中定义文本,则它将**返回**到本地化的key(ASP.NET Core的标准行为). + +运行该应用程序,看到新菜单项已添加到顶部栏: + +![bookstore-menu-items](./images/bookstore-new-menu-item.png) + +点击BookStore下Books子菜单项就会跳转到新增的书籍页面. + +#### 书籍列表 + +我们将使用[Datatables.net](https://datatables.net/)JQuery插件来显示页面上的表格列表. [Datatables](https://datatables.net/)可以完全通过AJAX工作,速度快,并提供良好的用户体验. Datatables插件在启动模板中配置,因此你可以直接在任何页面中使用它,而需要在页面中引用样式和脚本文件. + +##### Index.cshtml + +将`Pages/Books/Index.cshtml`改成下面的样子: + +````html +@page +@inherits Acme.BookStore.Web.Pages.BookStorePage +@model Acme.BookStore.Web.Pages.Books.IndexModel +@section scripts +{ + +} + + +

@L["Books"]

+
+ + + + + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + + + + +
+```` + +* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro)用于将外部的 **脚本** 添加到页面中.它比标准的`script`标签多了很多额外的功能.它可以处理 **最小化**和 **版本**.查看[捆绑 & 压缩文档](https://docs.abp.io/zh-Hans/abp/latest/UI/AspNetCore/Bundling-Minification)获取更多信息. +* `abp-card` 和 `abp-table` 是为Twitter Bootstrap的[card component](http://getbootstrap.com/docs/4.1/components/card/)封装的 **tag helpers**.ABP中有很多tag helpers,可以很方便的使用大多数[bootstrap](https://getbootstrap.com/)组件.你也可以使用原生的HTML标签代替tag helpers.使用tag helper可以通过智能提示和编译时类型检查减少HTML代码并防止错误.查看[tag helpers 文档](https://docs.abp.io/zh-Hans/abp/latest/UI/AspNetCore/Tag-Helpers/Index). +* 你可以像上面本地化菜单一样 **本地化** 列名. + +#### 添加脚本文件 + +在`Pages/Books/`文件夹中创建 `index.js`文件 + +![bookstore-index-js-file](./images/bookstore-index-js-file-v2.png) + +`index.js`的内容如下: + +````js +$(function () { + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); +}); +```` + +* `abp.libs.datatables.createAjax`是帮助ABP的动态JavaScript API代理跟[Datatable](https://datatables.net/)的格式相适应的辅助方法. +* `abp.libs.datatables.normalizeConfiguration`是另一个辅助方法.不是必须的, 但是它通过为缺少的选项提供常规值来简化数据表配置. +* `acme.bookStore.book.getList`是获取书籍列表的方法(上面已经介绍过了) +* 查看 [Datatable文档](https://datatables.net/manual/) 了解更多配置项. + +最终的页面如下: + +![Book list](./images/bookstore-book-list-2.png) + +{{end}} + +{{if UI == "NG"}} + +### Angular 开发 + +#### 创建books页面 + +是时候创建可见和可用的东西了!开发ABP Angular前端应用程序时,需要使用一些工具: + +- [Angular CLI](https://angular.io/cli) 用于创建模块,组件和服务. +- [NGXS](https://ngxs.gitbook.io/ngxs/) 用于管理状态库. +- [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) 用做UI组件库. +- [Visual Studio Code](https://code.visualstudio.com/) 用做代码编辑器 (你可以选择自己喜欢的编辑器). + +#### 安装 NPM 包 + +在 `angular` 目录下打开命令行窗口,选择 `yarn` 命令安装NPM包: + +```bash +yarn +``` + +#### BooksModule + +运行以下命令创建一个名为 `BooksModule` 的新模块: + +```bash +yarn ng generate module books --route books --module app.module +``` + +![Generating books module](./images/bookstore-creating-books-module-terminal.png) + +#### 路由 + +打开位于 `src\app` 目录下的 `app-routing.module.ts` 文件. 添加新的 `import` 和替换 `books` 路径: + +```js +import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; //==> added this line to imports <== + +//...replaced original books path with the below +{ + path: 'books', + component: ApplicationLayoutComponent, + loadChildren: () => import('./books/books.module').then(m => m.BooksModule), + data: { + routes: { + name: '::Menu:Books', + iconClass: 'fas fa-book' + } as ABP.Route + }, +} +``` + +* `ApplicationLayoutComponent` 配置将应用程序布局设置为新页面, 我们添加了 `data` 对象. `name` 是菜单项的名称,`iconClass` 是菜单项的图标. + +运行 `yarn start` 等待Angular为应用程序启动服务: + +```bash +yarn start +``` + +打开浏览器导航到 http://localhost:4200/books. 你会看到一个带有 "*books works!*" 的空白页. + +![initial-books-page](./images/bookstore-initial-books-page-with-layout.png) + +#### Book 列表组件 + +用以下内容替换 `books.component.html`: + +```html + +``` + +在命令行运行以下命令,生成名为 book-list 的新组件: + +```bash +yarn ng generate component books/book-list +``` + +![Creating books list](./images/bookstore-creating-book-list-terminal.png) + +打开 `app\books` 目录下的 `books.module.ts` 文件,使用以下内容替换它: + +```js +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { BooksRoutingModule } from './books-routing.module'; +import { BooksComponent } from './books.component'; +import { BookListComponent } from './book-list/book-list.component'; +import { SharedModule } from '../shared/shared.module'; //<== added this line ==> + +@NgModule({ + declarations: [BooksComponent, BookListComponent], + imports: [ + CommonModule, + BooksRoutingModule, + SharedModule, //<== added this line ==> + ] +}) +export class BooksModule { } +``` + +* 我们导入了 `SharedModule` 并添加到 `imports` 数组. + +打开 `app\books` 目录下的 `books-routing.module.ts` 文件用以下内容替换它: + +```js +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { BooksComponent } from './books.component'; +import { BookListComponent } from './book-list/book-list.component'; //<== added this line ==> + +//<== replaced routes ==> +const routes: Routes = [ + { + path: '', + component: BooksComponent, + children: [{ path: '', component: BookListComponent }], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class BooksRoutingModule { } +``` + +* 我们导入了 `BookListComponent` 并替换 `routes` 常量. + +我们将看到books页面的 **book-list works!**: + +![Initial book list page](./images/bookstore-initial-book-list-page.png) + +#### 创建 BooksState + +运行以下命令创建名为 `BooksState` 的新state: + +```bash +npx @ngxs/cli --name books --directory src/app/books +``` + +* 此命令在 `src/app/books/state` 文件夹下创建了 `books.state.ts` 和 `books.actions.ts` 文件. 参阅 [NGXS CLI文档](https://www.ngxs.io/plugins/cli)了解更多. + +将 `BooksState` 导入到 `src/app` 文件夹中的 `app.module.ts` 中. 然后添加 `BooksState` 到 `NgxsModule` 的 `forRoot` 静态方法,作为该方法的第一个参数的数组元素. + +```js +// ... +import { BooksState } from './books/state/books.state'; //<== imported BooksState ==> + +@NgModule({ + imports: [ + // other imports + + NgxsModule.forRoot([BooksState]), //<== added BooksState ==> + + //other imports + ], + // ... +}) +export class AppModule {} +``` + +#### 生成代理 + +ABP CLI提供了 `generate-proxy` 命令为你的服务HTTP API生成客户端代理简化客户端使用服务的成本. 运行 `generate-proxy` 命令前你的host必须正在运行. 参阅 [CLI 文档](../CLI.md). + +在 `angular` 文件夹下运行以下命令: + +```bash +abp generate-proxy --module app +``` + +![Generate proxy command](./images/generate-proxy-command.png) + +生成的文件如下: + +![Generated files](./images/generated-proxies.png) + +#### GetBooks 动作 + +动作可以被认为是一个命令,它应该触发某些事情发生,或者是已经发生的事情的结果事件.[See NGXS Actions文档](https://www.ngxs.io/concepts/actions). + +打开 `app/books/state` 目录下的 `books.actions.ts` 文件用以下内容替换它: + +```js +export class GetBooks { + static readonly type = '[Books] Get'; +} +``` + +#### 实现 BooksState + +打开 `app/books/state` 目录下的 `books.state.ts` 文件用以下内容替换它: + +```js +import { PagedResultDto } from '@abp/ng.core'; +import { State, Action, StateContext, Selector } from '@ngxs/store'; +import { GetBooks } from './books.actions'; +import { BookService } from '../../app/shared/services'; +import { tap } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; + +export class BooksStateModel { + public book: PagedResultDto; +} + +@State({ + name: 'BooksState', + defaults: { book: {} } as BooksStateModel, +}) +@Injectable() +export class BooksState { + @Selector() + static getBooks(state: BooksStateModel) { + return state.book.items || []; + } + + constructor(private bookService: BookService) {} + + @Action(GetBooks) + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((booksResponse) => { + ctx.patchState({ + book: booksResponse, + }); + }) + ); + } +} +``` + +* 我们添加了book属性到BooksStateModel模态框. +* 我们添加了 `GetBooks` 动作. 它通过 ABP CLI生成的 `BooksService` 检索图书数据. +* `NGXS` 需要在不订阅get函数的情况下返回被观察对象. + +#### BookListComponent + +打开 `app\books\book-list` 目录下的 `book-list.component.ts` 用以下内容替换它: + +```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 '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; + +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + loading = false; + + constructor(private store: Store) {} + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } +} +``` + +* 我们添加了 `get` 函数获取books更新store. +* 有关 `NGXS` 特性的更多信息请参见NGXS文档中的[Dispatching actions](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions)和[Select](https://ngxs.gitbook.io/ngxs/concepts/select). + +打开 `app\books\book-list` 目录下的 `book-list.component.html` 用以下内容替换它: + +```html +
+
+
+
+
+ {%{{{ "::Menu:Books" | abpLocalization }}}%} +
+
+
+
+
+
+ + + + + {%{{{ "::Name" | abpLocalization }}}%} + {%{{{ "::Type" | abpLocalization }}}%} + {%{{{ "::PublishDate" | abpLocalization }}}%} + {%{{{ "::Price" | abpLocalization }}}%} + + + + + {%{{{ data.name }}}%} + {%{{{ booksType[data.type] }}}%} + {%{{{ data.publishDate | date }}}%} + {%{{{ data.price }}}%} + + +
+
+``` + +* 我们添加了图书列表页面的HTML代码. + +现在你可以在浏览器看到最终结果: + +![Book list final result](./images/bookstore-book-list.png) + +项目的文件系统结构: + +![Book list final result](./images/bookstore-angular-file-tree.png) + +在本教程中我们遵循了官方的[Angular风格指南](https://angular.io/guide/styleguide#file-tree). + +{{end}} + +### 下一章 + +参阅[第二章](part-2.md)了解创建,更新和删除图书. \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/Part-2.md b/docs/zh-Hans/Tutorials/Part-2.md new file mode 100644 index 0000000000..56dadcbbcd --- /dev/null +++ b/docs/zh-Hans/Tutorials/Part-2.md @@ -0,0 +1,1349 @@ +## ASP.NET Core {{UI_Value}} 教程 - 第二章 +````json +//[doc-params] +{ + "UI": ["MVC","NG"] +} +```` + +{{ +if UI == "MVC" + DB="ef" + DB_Text="Entity Framework Core" + UI_Text="mvc" +else if UI == "NG" + DB="mongodb" + DB_Text="MongoDB" + UI_Text="angular" +else + DB ="?" + UI_Text="?" +end +}} + +### 关于本教程 + +这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章: + +- [Part-1: 创建项目和书籍列表页面](Part-1.md) +- **Part 2: 创建,编辑,删除书籍(本章)** +- [Part-3: 集成测试](Part-3.md) + +> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). + +{{if UI == "MVC"}} + +### 新增 Book 实体 + +通过本节, 你将会了解如何创建一个 modal form 来实现新增书籍的功能. 最终成果如下图所示: + +![bookstore-create-dialog](./images/bookstore-create-dialog-2.png) + +#### 新建 modal form + +在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面: + +![bookstore-add-create-dialog](./images/bookstore-add-create-dialog-v2.png) + +##### CreateModal.cshtml.cs + +打开 `CreateModal.cshtml.cs` 代码文件,用如下代码替换 `CreateModalModel` 类的实现: + +````C# +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Web.Pages.Books +{ + public class CreateModalModel : BookStorePageModel + { + [BindProperty] + public CreateUpdateBookDto Book { get; set; } + + private readonly IBookAppService _bookAppService; + + public CreateModalModel(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnPostAsync() + { + await _bookAppService.CreateAsync(Book); + return NoContent(); + } + } +} +```` + +* 该类派生于 `BookStorePageModel` 而非默认的 `PageModel`. `BookStorePageModel` 继承了 `PageModel` 并且添加了一些可以被你的page model类使用的通用属性和方法. +* `Book` 属性上的 `[BindProperty]` 特性将post请求提交上来的数据绑定到该属性上. +* 该类通过构造函数注入了 `IBookAppService` 应用服务,并且在 `OnPostAsync` 处理程序中调用了服务的 `CreateAsync` 方法. + +##### CreateModal.cshtml + +打开 `CreateModal.cshtml` 文件并粘贴如下代码: + +````html +@page +@inherits Acme.BookStore.Web.Pages.BookStorePage +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model Acme.BookStore.Web.Pages.Books.CreateModalModel +@{ + Layout = null; +} + + + + + + + + + +```` + +* 这个 modal 使用 `abp-dynamic-form` Tag Helper 根据 `CreateBookViewModel` 类自动构建了表单. + * `abp-model` 指定了 `Book` 属性为模型对象. + * `data-ajaxForm` 设置了表单通过AJAX提交,而不是经典的页面回发. + * `abp-form-content` tag helper 作为表单控件渲染位置的占位符 (这是可选的,只有你在 `abp-dynamic-form` 中像本示例这样添加了其他内容才需要). + +#### 添加 "New book" 按钮 + +打开 `Pages/Books/Index.cshtml` 并按如下代码修改 `abp-card-header` : + +````html + + + +

@L["Books"]

+
+ + + +
+
+```` + +如下图所示,只是在表格 **右上方** 添加了 **New book** 按钮: + +![bookstore-new-book-button](./images/bookstore-new-book-button.png) + +打开 `Pages/books/index.js` 在 `datatable` 配置代码后面添加如下代码: + +````js +var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + +createModal.onResult(function () { + dataTable.ajax.reload(); +}); + +$('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); +}); +```` + +* `abp.ModalManager` 是一个在客户端打开和管理modal的辅助类.它基于Twitter Bootstrap的标准modal组件通过简化的API抽象隐藏了许多细节. + +现在,你可以 **运行程序** 通过新的 modal form 来创建书籍了. + +### 编辑更新已存在的 Book 实体 + +在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个名叫 `EditModal.cshtml` 的Razor页面: + +![bookstore-add-edit-dialog](./images/bookstore-add-edit-dialog.png) + +#### EditModal.cshtml.cs + +展开 `EditModal.cshtml`,打开 `EditModal.cshtml.cs` 文件( `EditModalModel` 类) 并替换成以下代码: + +````csharp +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Web.Pages.Books +{ + public class EditModalModel : BookStorePageModel + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid Id { get; set; } + + [BindProperty] + public CreateUpdateBookDto Book { get; set; } + + private readonly IBookAppService _bookAppService; + + public EditModalModel(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync() + { + var bookDto = await _bookAppService.GetAsync(Id); + Book = ObjectMapper.Map(bookDto); + } + + public async Task OnPostAsync() + { + await _bookAppService.UpdateAsync(Id, Book); + return NoContent(); + } + } +} +```` + +* `[HiddenInput]` 和 `[BindProperty]` 是标准的 ASP.NET Core MVC 特性.这里启用 `SupportsGet` 从Http请求的查询字符串中获取Id的值. +* 在 `OnGetAsync` 方法中,将 `BookAppService.GetAsync` 方法返回的 `BookDto` 映射成 `CreateUpdateBookDto` 并赋值给Book属性. +* `OnPostAsync` 方法直接使用 `BookAppService.UpdateAsync` 来更新实体. + +#### BookDto到CreateUpdateBookDto对象映射 + +为了执行`BookDto`到`CreateUpdateBookDto`对象映射,请打开`Acme.BookStore.Web`项目中的`BookStoreWebAutoMapperProfile.cs`并更改它,如下所示: + +````csharp +using AutoMapper; + +namespace Acme.BookStore.Web +{ + public class BookStoreWebAutoMapperProfile : Profile + { + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + } + } +} +```` + +* 刚刚添加了`CreateMap();`作为映射定义. + +#### EditModal.cshtml + +将 `EditModal.cshtml` 页面内容替换成如下代码: + +````html +@page +@inherits Acme.BookStore.Web.Pages.BookStorePage +@using Acme.BookStore.Web.Pages.Books +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@{ + Layout = null; +} + + + + + + + + + + +```` + +这个页面内容和 `CreateModal.cshtml` 非常相似,除了以下几点: + +* 它包含`id`属性的`abp-input`, 用于存储编辑书的 `id` (它是隐藏的Input) +* 此页面指定的post地址是`Books/EditModal`, 并用文本 *Update* 作为 modal 标题. + +#### 为表格添加 "操作(Actions)" 下拉菜单 + +我们将为表格每行添加下拉按钮 ("Actions") . 最终效果如下: + +![bookstore-books-table-actions](images/bookstore-books-table-actions.png) + +打开 `Pages/Books/Index.cshtml` 页面,并按下方所示修改表格部分的代码: + +````html + + + + @L["Actions"] + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + + + +```` + +* 只是为"Actions"增加了一个 `th` 标签. + +打开 `Pages/books/index.js` 并用以下内容进行替换: + +````js +$(function () { + + var l = abp.localization.getResource('BookStore'); + + var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); + + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + processing: true, + serverSide: true, + paging: true, + searching: false, + autoWidth: false, + scrollCollapse: true, + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + } + ] + } + }, + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +* 通过 `abp.localization.getResource('BookStore')` 可以在客户端使用服务器端定义的相同的本地化语言文本. +* 添加了一个名为 `createModal` 的新的 `ModalManager` 来打开创建用的 modal 对话框. +* 添加了一个名为 `editModal` 的新的 `ModalManager` 来打开编辑用的 modal 对话框. +* 在 `columnDefs` 起始处新增一列用于显示 "Actions" 下拉按钮. +* "New Book"动作只需调用`createModal.open`来打开创建对话框. +* "Edit" 操作只是简单调用 `editModal.open` 来打开编辑对话框. + +现在,你可以运行程序,通过编辑操作来更新任一个book实体. + +### 删除一个已有的Book实体 + +打开 `Pages/books/index.js` 文件,在 `rowAction` `items` 下新增一项: + +````js +{ + text: l('Delete'), + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function() { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } +} +```` + +* `confirmMessage` 用来在实际执行 `action` 之前向用户进行确认. +* 通过javascript代理方法 `acme.bookStore.book.delete` 执行一个AJAX请求来删除一个book实体. +* `abp.notify.info` 用来在执行删除操作后显示一个toastr通知信息. + +最终的 `index.js` 文件内容如下所示: + +````js +$(function () { + + var l = abp.localization.getResource('BookStore'); + + var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); + + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + processing: true, + serverSide: true, + paging: true, + searching: false, + autoWidth: false, + scrollCollapse: true, + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: l('Delete'), + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function() { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +打开`Acme.BookStore.Domain.Shared`项目中的`en.json`并添加以下行: + +````json +"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?", +"SuccessfullyDeleted": "Successfully deleted" +```` + +运行程序并尝试删除一个book实体. + +{{end}} + +### 新增 Book 实体 + +下面的章节中,你将学习到如何创建一个新的模态对话框来新增Book实体. + +#### 状态定义 + +在 `books\state` 文件夹下打开 `books.action.ts` 文件,使用以下内容替换它: + +```js +import { CreateUpdateBookDto } from '../../app/shared/models'; //<== added this line ==> + +export class GetBooks { + static readonly type = '[Books] Get'; +} + +// added CreateUpdateBook class +export class CreateUpdateBook { + static readonly type = '[Books] Create Update Book'; + constructor(public payload: CreateUpdateBookDto) { } +} +``` + +* 我们导入了 `CreateUpdateBookDto` 模型并且创建了 `CreateUpdateBook` 动作. + +打开 `books\state` 文件夹下的 `books.state.ts` 文件,使用以下内容替换它: + +```js +import { PagedResultDto } from '@abp/ng.core'; +import { State, Action, StateContext, Selector } from '@ngxs/store'; +import { GetBooks, CreateUpdateBook } from './books.actions'; // <== added CreateUpdateBook==> +import { BookService } from '../../app/shared/services'; +import { tap } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; + +export class BooksStateModel { + public book: PagedResultDto; +} + +@State({ + name: 'BooksState', + defaults: { book: {} } as BooksStateModel, +}) +@Injectable() +export class BooksState { + @Selector() + static getBooks(state: BooksStateModel) { + return state.book.items || []; + } + + constructor(private bookService: BookService) {} + + @Action(GetBooks) + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((bookResponse) => { + ctx.patchState({ + book: bookResponse, + }); + }) + ); + } + + // added CreateUpdateBook action listener + @Action(CreateUpdateBook) + save(ctx: StateContext, action: CreateUpdateBook) { + return this.bookService.createByInput(action.payload); + } +} +``` + +* 我们导入了 `CreateUpdateBook` 动作并且定义了 `save` 方法监听 `CreateUpdateBook` 动作去创建图书. + +当 `SaveBook` 动作被分派时,save方法被执行. 它调用 `BookService` 的 `createByInput` 方法. + +#### 添加模态到 BookListComponent + +打开 `books\book-list` 文件夹内的 `book-list.component.html` 文件,使用以下内容替换它: + +```html +
+
+
+
+
+ {%{{{ '::Menu:Books' | abpLocalization }}}%} +
+
+ +
+
+ +
+
+
+
+
+ + + + + {%{{{ "::Name" | abpLocalization }}}%} + {%{{{ "::Type" | abpLocalization }}}%} + {%{{{ "::PublishDate" | abpLocalization }}}%} + {%{{{ "::Price" | abpLocalization }}}%} + + + + + {%{{{ data.name }}}%} + {%{{{ booksType[data.type] }}}%} + {%{{{ data.publishDate | date }}}%} + {%{{{ data.price }}}%} + + +
+
+ + + + +

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

+
+ + + + + + +
+``` + +* 我们添加了 `abp-modal` 渲染模态框,允许用户创建新书. +* `abp-modal` 是显示模态框的预构建组件. 你也可以使用其它方法显示模态框,但 `abp-modal` 提供了一些附加的好处. +* 我们添加了 `New book` 按钮到 `AbpContentToolbar`. + +打开 `books\book-list` 文件夹下的 `book-list.component.ts` 文件,使用以下内容替换它: + +```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 '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; + +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + loading = false; + + isModalOpen = false; // <== added this line ==> + + constructor(private store: Store) {} + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } + + // added createBook method + createBook() { + this.isModalOpen = true; + } +} +``` + +* 我们添加了 `isModalOpen = false` 和 `createBook` 方法. + +你可以打开浏览器,点击**New book**按钮看到模态框. + +![Empty modal for new book](./images/bookstore-empty-new-book-modal.png) + +#### 添加响应式表单 + +[响应式表单](https://angular.io/guide/reactive-forms) 提供一种模型驱动的方法来处理其值随时间变化的表单输入. + +打开 `app\books\book-list` 文件夹下的 `book-list.component.ts` 文件,使用以下内容替换它: + +```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 '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; +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'], +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + loading = false; + + isModalOpen = false; + + form: FormGroup; // <== added this line ==> + + constructor(private store: Store, private fb: FormBuilder) {} // <== added FormBuilder ==> + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } + + createBook() { + this.buildForm(); //<== added this line ==> + this.isModalOpen = true; + } + + // added buildForm method + buildForm() { + this.form = this.fb.group({ + name: ['', Validators.required], + type: [null, Validators.required], + publishDate: [null, Validators.required], + price: [null, Validators.required], + }); + } +} +``` + +* 我们导入了 `FormGroup, FormBuilder and Validators`. +* 我们添加了 `form: FormGroup` 变量. +* 我们注入了 `fb: FormBuilder` 服务到构造函数. [FormBuilder](https://angular.io/api/forms/FormBuilder) 服务为生成控件提供了方便的方法. 它减少了构建复杂表单所需的样板文件的数量. +* 我们添加了 `buildForm` 方法到文件末尾, 在 `createBook` 方法调用 `buildForm()` 方法. 该方法创建一个响应式表单去创建新书. + * `FormBuilder` 内的 `fb.group` 方法创建一个 `FormGroup`. + * 添加了 `Validators.required` 静态方法用于验证表单元素. + +#### 创建表单的DOM元素 + +打开 `app\books\book-list` 文件夹下的 `book-list.component.html` 文件,使用以下内容替换 ` `: + +```html + + +
+ * + +
+ +
+ * + +
+ +
+ * + +
+ +
+ * + +
+ +
+``` + +- 模板创建了带有 `Name`, `Price`, `Type` 和 `Publish` 时间字段的表单. +- 我们在组件中使用了 [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview). + +#### Datepicker 要求 + +打开 `app\books` 文件夹下的 `books.module.ts` 文件,使用以下内容替换它: + +```js +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { BooksRoutingModule } from './books-routing.module'; +import { BooksComponent } from './books.component'; +import { BookListComponent } from './book-list/book-list.component'; +import { SharedModule } from '../shared/shared.module'; +import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; //<== added this line ==> + +@NgModule({ + declarations: [BooksComponent, BookListComponent], + imports: [ + CommonModule, + BooksRoutingModule, + SharedModule, + NgbDatepickerModule //<== added this line ==> + ] +}) +export class BooksModule { } +``` + +* 我们导入了 `NgbDatepickerModule` 来使用日期选择器. + +打开 `app\books\book-list` 文件夹下的 `book-list.component.ts` 文件,使用以下内容替换它: + +```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 '../../app/shared/models'; +import { GetBooks } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; // <== added this line ==> + +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], + providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], // <== added this line ==> +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + //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) {} + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } + + createBook() { + this.buildForm(); + this.isModalOpen = true; + } + + buildForm() { + this.form = this.fb.group({ + name: ['', Validators.required], + type: [null, Validators.required], + publishDate: [null, Validators.required], + price: [null, Validators.required], + }); + } +} +``` + +* 我们导入了 ` NgbDateNativeAdapter, NgbDateAdapter` + +* 我们添加了一个新的 `NgbDateAdapter` 提供程序,它将Datepicker值转换为Date类型. 有关更多详细信息,请参见[datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview). + +* 我们添加了 `bookTypeArr` 数组,以便能够在combobox值中使用它. `bookTypeArr` 包含 `BookType` 枚举的字段. 得到的数组如下所示: + + ```js + ['Adventure', 'Biography', 'Dystopia', 'Fantastic' ...] + ``` + + 在先前的表单模板中 用 `ngFor` 使用这个数组. + +现在你可以打开浏览器看到以下变化: + +![New book modal](./images/bookstore-new-book-form.png) + +#### 保存图书 + +打开 `app\books\book-list` 文件夹下的 `book-list.component.ts` 文件,使用以下内容替换它: + +```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 '../../app/shared/models'; +import { GetBooks, CreateUpdateBook } from '../state/books.actions'; // <== added CreateUpdateBook ==> +import { BooksState } from '../state/books.state'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], + providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + //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) {} + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } + + createBook() { + this.buildForm(); + this.isModalOpen = true; + } + + buildForm() { + this.form = this.fb.group({ + name: ['', Validators.required], + type: [null, Validators.required], + publishDate: [null, Validators.required], + price: [null, Validators.required], + }); + } + + //<== added save ==> + save() { + if (this.form.invalid) { + return; + } + + this.store.dispatch(new CreateUpdateBook(this.form.value)).subscribe(() => { + this.isModalOpen = false; + this.form.reset(); + this.get(); + }); + } +} +``` + +* 我们导入了 `CreateUpdateBook`. +* 我们添加了 `save` 方法. + +打开 `app\books\book-list` 文件夹下的 `app\books\book-list`文件, 添加 `abp-button` 保存图书. + +```html + + + + + + +``` + +使用以下内容替换 `
` 标签: + +```html + +``` + +* 我们添加了 `(ngSubmit)="save()"` 到 `` 元素,当按下enter时保存图书. +* 我们在模态框的底部添加了 `abp-button` 来保存图书. + +模态框最终看起来像这样: + +![Save button to the modal](./images/bookstore-new-book-form-v2.png) + +### 更新图书 + +#### CreateUpdateBook 动作 + +打开 `books\state` 文件夹下的 `books.actions.ts` 文件,使用以下内容替换它: + +```js +import { CreateUpdateBookDto } from '../../app/shared/models'; + +export class GetBooks { + static readonly type = '[Books] Get'; +} + +export class CreateUpdateBook { + static readonly type = '[Books] Create Update Book'; + constructor(public payload: CreateUpdateBookDto, public id?: string) { } // <== added id parameter ==> +} +``` + +* 我们在 `CreateUpdateBook` 动作的构造函数添加了 `id` 参数. + +打开 `books\state` 文件夹下的 `books.state.ts` 文件,使用以下内容替换 `save` 方法: + +```js +@Action(CreateUpdateBook) +save(ctx: StateContext, action: CreateUpdateBook) { + if (action.id) { + return this.bookService.updateByIdAndInput(action.payload, action.id); + } else { + return this.bookService.createByInput(action.payload); + } +} +``` + +#### BookListComponent + +打开 `app\books\book-list` 文件夹下的 `book-list.component.ts` 文件,在构造函数注入 `BookService` 服务,并添加 名为 `selectedBook` 的变量. + +```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 '../../app/shared/models'; +import { GetBooks, CreateUpdateBook } from '../state/books.actions'; +import { BooksState } from '../state/books.state'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { BookService } from '../../app/shared/services'; // <== imported BookService ==> + +@Component({ + selector: 'app-book-list', + templateUrl: './book-list.component.html', + styleUrls: ['./book-list.component.scss'], + providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }], +}) +export class BookListComponent implements OnInit { + @Select(BooksState.getBooks) + books$: Observable; + + booksType = BookType; + + bookTypeArr = Object.keys(BookType).filter( + (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 ==> + + ngOnInit() { + this.get(); + } + + get() { + this.loading = true; + this.store + .dispatch(new GetBooks()) + .pipe(finalize(() => (this.loading = false))) + .subscribe(() => {}); + } + + // <== this method is replaced ==> + createBook() { + this.selectedBook = {} as BookDto; // <== added ==> + this.buildForm(); + this.isModalOpen = true; + } + + // <== added editBook method ==> + editBook(id: string) { + this.bookService.getById(id).subscribe((book) => { + this.selectedBook = book; + this.buildForm(); + this.isModalOpen = true; + }); + } + + // <== this method is replaced ==> + buildForm() { + this.form = this.fb.group({ + name: [this.selectedBook.name || '', Validators.required], + type: [this.selectedBook.type || null, Validators.required], + publishDate: [ + this.selectedBook.publishDate ? new Date(this.selectedBook.publishDate) : null, + Validators.required, + ], + price: [this.selectedBook.price || null, Validators.required], + }); + } + + 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(); + }); + } +} +``` + +* 我们导入了 `BookService`. +* 我们声明了类型为 `BookDto` 的 `selectedBook` 变量. +* 我们在构造函数注入了 `BookService`, 它用于检索正在编辑的图书数据. +* 我们添加了 `editBook` 方法, 根据给定图书 `Id` 设置 `selectedBook` 对象. +* 我们替换了 `buildForm` 方法使用 `selectedBook` 数据创建表单. +* 我们替换了 `createBook` 方法,设置 `selectedBook` 为空对象. +* 我们在 `CreateUpdateBook` 构造函数添加了 `selectedBook.id`. + +#### 添加 "Actions" 下拉框到表格 + +打开 `app\books\book-list` 文件夹下的 `book-list.component.html` 文件,使用以下内容替换 `
` 标签: + +```html +
+ + + + + {%{{{ "::Actions" | abpLocalization }}}%} + {%{{{ "::Name" | abpLocalization }}}%} + {%{{{ "::Type" | abpLocalization }}}%} + {%{{{ "::PublishDate" | abpLocalization }}}%} + {%{{{ "::Price" | abpLocalization }}}%} + + + + + +
+ +
+ +
+
+ + {%{{{ data.name }}}%} + {%{{{ booksType[data.type] }}}%} + {%{{{ data.publishDate | date }}}%} + {%{{{ data.price }}}%} + +
+
+``` + +- 我们添加了 "Actions" 栏的 `th`. +- 我们添加了带有 `ngbDropdownToggle` 的 `button`,在点击按钮时打开操作. +- 我们习惯于将[NgbDropdown](https://ng-bootstrap.github.io/#/components/dropdown/examples)用于操作的下拉菜单. + +UI最终看起来像这样: + +![Action buttons](./images/bookstore-actions-buttons.png) + +打开 `app\books\book-list` 文件夹下的 `book-list.component.html` 文件,使用以下内容替换 `` 标签: + +```html + +

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

+
+``` + +* **Edit** 文本做为编辑记录操作的标题, **New Book** 做为添加记录操作的标题. + +### 删除图书 + +#### DeleteBook 动作 + +打开 `books\state` 文件夹下的 `books.actions.ts` 文件添加名为 `DeleteBook` 的动作. + +```js +export class DeleteBook { + static readonly type = '[Books] Delete'; + constructor(public id: string) {} +} +``` + +打开 `books\state` 文件夹下的 `books.state.ts` 文件,使用以下内容替换它: + +```js +import { PagedResultDto } from '@abp/ng.core'; +import { State, Action, StateContext, Selector } from '@ngxs/store'; +import { GetBooks, CreateUpdateBook, DeleteBook } from './books.actions'; // <== added DeleteBook==> +import { BookService } from '../../app/shared/services'; +import { tap } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; +import { BookDto } from '../../app/shared/models'; + +export class BooksStateModel { + public book: PagedResultDto; +} + +@State({ + name: 'BooksState', + defaults: { book: {} } as BooksStateModel, +}) +@Injectable() +export class BooksState { + @Selector() + static getBooks(state: BooksStateModel) { + return state.book.items || []; + } + + constructor(private bookService: BookService) {} + + @Action(GetBooks) + get(ctx: StateContext) { + return this.bookService.getListByInput().pipe( + tap((booksResponse) => { + ctx.patchState({ + book: booksResponse, + }); + }) + ); + } + + @Action(CreateUpdateBook) + save(ctx: StateContext, action: CreateUpdateBook) { + if (action.id) { + return this.bookService.updateByIdAndInput(action.payload, action.id); + } else { + return this.bookService.createByInput(action.payload); + } + } + + // <== added DeleteBook action listener ==> + @Action(DeleteBook) + delete(ctx: StateContext, action: DeleteBook) { + return this.bookService.deleteById(action.id); + } +} +``` + +- 我们导入了 `DeleteBook` . + +- 我们在文件末尾添加了 `DeleteBook` 动作监听器. + +#### 删除确认弹层 + +打开 `app\books\book-list` 文件夹下的 `book-list.component.ts` 文件,注入 `ConfirmationService`. + +替换构造函数: + +```js +import { ConfirmationService } from '@abp/ng.theme.shared'; +//... + +constructor( + private store: Store, + private fb: FormBuilder, + private bookService: BookService, + private confirmation: ConfirmationService // <== added this line ==> +) { } +``` + +* 我们导入了 `ConfirmationService`. +* 我们在构造函数注入了 `ConfirmationService` . + +参阅[确认弹层文档](https://docs.abp.io/en/abp/latest/UI/Angular/Confirmation-Service)了解更多 + +在 `book-list.component.ts` 中添加删除方法: + +```js +import { GetBooks, CreateUpdateBook, DeleteBook } from '../state/books.actions' ;// <== imported DeleteBook ==> + +import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; //<== imported Confirmation ==> + +//... + +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()); + } + }); +} +``` + +`delete` 方法会显示一个确认弹层并订阅用户响应. 只在用户点击 `Yes` 按钮时分派动作. 确认弹层看起来如下: + +![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png) + +#### 添加删除按钮 + +打开 `app\books\book-list` 文件夹下的 `app\books\book-list` 文件,修改 `ngbDropdownMenu` 添加删除按钮: + +```html +
+ + +
+``` + +最终操作下拉框UI看起来如下: + +![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png) + +{{end}} + +### 下一章 + +查看本教程的 [下一章](Part-3.md) . diff --git a/docs/zh-Hans/Tutorials/Part-3.md b/docs/zh-Hans/Tutorials/Part-3.md new file mode 100644 index 0000000000..cded954e6b --- /dev/null +++ b/docs/zh-Hans/Tutorials/Part-3.md @@ -0,0 +1,198 @@ +## ASP.NET Core {{UI_Value}} 教程 - 第三章 +````json +//[doc-params] +{ + "UI": ["MVC","NG"] +} +```` + +{{ +if UI == "MVC" + DB="ef" + DB_Text="Entity Framework Core" + UI_Text="mvc" +else if UI == "NG" + DB="mongodb" + DB_Text="MongoDB" + UI_Text="angular" +else + DB ="?" + UI_Text="?" +end +}} + +### 关于本教程 + +这是ASP.NET Core{{UI_Value}}系列教程的第二章. 共有三章: + +- [Part-1: 创建项目和书籍列表页面](Part-1.md) +- [Part 2: 创建,编辑,删除书籍](Part-2.md) +- **Part-3: 集成测试(本章)** + +> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). + +### 解决方案中的测试项目 + +解决方案中有多个测试项目: + +![bookstore-test-projects-v2](./images/bookstore-test-projects-{{UI_Text}}.png) + +每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试: + +* [xunit](https://xunit.github.io/) 作为主测试框架. +* [Shoudly](http://shouldly.readthedocs.io/en/latest/) 作为断言库. +* [NSubstitute](http://nsubstitute.github.io/) 作为模拟库. + +### 添加测试数据 + +启动模板包含`Acme.BookStore.TestBase`项目中的`BookStoreTestDataSeedContributor`类,它创建一些数据来运行测试. +更改`BookStoreTestDataSeedContributor`类如下所示: + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; + +namespace Acme.BookStore +{ + public class BookStoreTestDataSeedContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; + + public BookStoreTestDataSeedContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) + { + _bookRepository = bookRepository; + _guidGenerator = guidGenerator; + } + + public async Task SeedAsync(DataSeedContext context) + { + await _bookRepository.InsertAsync( + new Book(id: _guidGenerator.Create(), + name: "Test book 1", + type: BookType.Fantastic, + publishDate: new DateTime(2015, 05, 24), + price: 21 + ) + ); + + await _bookRepository.InsertAsync( + new Book(id: _guidGenerator.Create(), + name: "Test book 2", + type: BookType.Science, + publishDate: new DateTime(2014, 02, 11), + price: 15 + ) + ); + } + } +} +```` + +* 注入`IRepository`并在`SeedAsync`中使用它来创建两个书实体作为测试数据. +* 使用`IGuidGenerator`服务创建GUID. 虽然`Guid.NewGuid()`非常适合测试,但`IGuidGenerator`在使用真实数据库时还有其他特别重要的功能(参见[Guid生成文档](../Guid-Generation.md)了解更多信息). + +### 测试 BookAppService + +在 `Acme.BookStore.Application.Tests` 项目中创建一个名叫 `BookAppService_Tests` 的测试类: + +````csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Shouldly; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; +using Microsoft.EntityFrameworkCore.Internal; + +namespace Acme.BookStore +{ + public class BookAppService_Tests : BookStoreApplicationTestBase + { + private readonly IBookAppService _bookAppService; + + public BookAppService_Tests() + { + _bookAppService = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_List_Of_Books() + { + //Act + var result = await _bookAppService.GetListAsync( + new PagedAndSortedResultRequestDto() + ); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(b => b.Name == "Test book 1"); + } + } +} +```` + +* 测试方法 `Should_Get_List_Of_Books` 直接使用 `BookAppService.GetListAsync` 方法来获取用户列表,并执行检查. + +新增测试方法,用以测试创建一个合法book实体的场景: + +````C# +[Fact] +public async Task Should_Create_A_Valid_Book() +{ + //Act + var result = await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + Name = "New test book 42", + Price = 10, + PublishDate = DateTime.Now, + Type = BookType.ScienceFiction + } + ); + + //Assert + result.Id.ShouldNotBe(Guid.Empty); + result.Name.ShouldBe("New test book 42"); +} +```` + +新增测试方法,用以测试创建一个非法book实体失败的场景: + +````csharp +[Fact] +public async Task Should_Not_Create_A_Book_Without_Name() +{ + var exception = await Assert.ThrowsAsync(async () => + { + await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + Name = "", + Price = 10, + PublishDate = DateTime.Now, + Type = BookType.ScienceFiction + } + ); + }); + + exception.ValidationErrors + .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name")); +} +```` + +* 由于 `Name` 是空值, ABP 抛出一个 `AbpValidationException` 异常. + +打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试: + +![bookstore-appservice-tests](./images/bookstore-appservice-tests.png) + +恭喜, 绿色图标表示测试已成功通过! diff --git a/docs/zh-Hans/Tutorials/images/bookstore-actions-buttons.png b/docs/zh-Hans/Tutorials/images/bookstore-actions-buttons.png new file mode 100644 index 0000000000..e8243fedc7 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-actions-buttons.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-add-create-dialog-v2.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png rename to docs/zh-Hans/Tutorials/images/bookstore-add-create-dialog-v2.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png b/docs/zh-Hans/Tutorials/images/bookstore-add-edit-dialog.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png rename to docs/zh-Hans/Tutorials/images/bookstore-add-edit-dialog.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-add-index-page-v2.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png rename to docs/zh-Hans/Tutorials/images/bookstore-add-index-page-v2.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-angular-file-tree.png b/docs/zh-Hans/Tutorials/images/bookstore-angular-file-tree.png new file mode 100644 index 0000000000..a3197b6457 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-angular-file-tree.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png b/docs/zh-Hans/Tutorials/images/bookstore-appservice-tests.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png rename to docs/zh-Hans/Tutorials/images/bookstore-appservice-tests.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-book-list-2.png b/docs/zh-Hans/Tutorials/images/bookstore-book-list-2.png new file mode 100644 index 0000000000..a460d4241b Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-book-list-2.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-book-list.png b/docs/zh-Hans/Tutorials/images/bookstore-book-list.png new file mode 100644 index 0000000000..9e6cc9e010 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-book-list.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png b/docs/zh-Hans/Tutorials/images/bookstore-books-table-actions.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png rename to docs/zh-Hans/Tutorials/images/bookstore-books-table-actions.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png b/docs/zh-Hans/Tutorials/images/bookstore-books-table.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png rename to docs/zh-Hans/Tutorials/images/bookstore-books-table.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-confirmation-popup.png b/docs/zh-Hans/Tutorials/images/bookstore-confirmation-popup.png new file mode 100644 index 0000000000..a80b180f1c Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-confirmation-popup.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog-2.png b/docs/zh-Hans/Tutorials/images/bookstore-create-dialog-2.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog-2.png rename to docs/zh-Hans/Tutorials/images/bookstore-create-dialog-2.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png b/docs/zh-Hans/Tutorials/images/bookstore-create-dialog.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png rename to docs/zh-Hans/Tutorials/images/bookstore-create-dialog.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-create-project-angular.png b/docs/zh-Hans/Tutorials/images/bookstore-create-project-angular.png new file mode 100644 index 0000000000..b9eb38b8b7 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-create-project-angular.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-create-project-mvc.png b/docs/zh-Hans/Tutorials/images/bookstore-create-project-mvc.png new file mode 100644 index 0000000000..f453b20279 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-create-project-mvc.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-creating-book-list-terminal.png b/docs/zh-Hans/Tutorials/images/bookstore-creating-book-list-terminal.png new file mode 100644 index 0000000000..6f19dcc7bf Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-creating-book-list-terminal.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-creating-books-module-terminal.png b/docs/zh-Hans/Tutorials/images/bookstore-creating-books-module-terminal.png new file mode 100644 index 0000000000..ec9ef4c42f Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-creating-books-module-terminal.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-database-tables-ef.png b/docs/zh-Hans/Tutorials/images/bookstore-database-tables-ef.png new file mode 100644 index 0000000000..857b10de5b Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-database-tables-ef.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-database-tables-mongodb.png b/docs/zh-Hans/Tutorials/images/bookstore-database-tables-mongodb.png new file mode 100644 index 0000000000..8d78bd9a54 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-database-tables-mongodb.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-edit-button.png b/docs/zh-Hans/Tutorials/images/bookstore-edit-button.png new file mode 100644 index 0000000000..bfc1c64797 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-edit-button.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-empty-new-book-modal.png b/docs/zh-Hans/Tutorials/images/bookstore-empty-new-book-modal.png new file mode 100644 index 0000000000..2a02802bb9 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-empty-new-book-modal.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-final-actions-dropdown.png b/docs/zh-Hans/Tutorials/images/bookstore-final-actions-dropdown.png new file mode 100644 index 0000000000..4f41829f0d Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-final-actions-dropdown.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-generate-state-books.png b/docs/zh-Hans/Tutorials/images/bookstore-generate-state-books.png new file mode 100644 index 0000000000..be7a919017 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-generate-state-books.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png b/docs/zh-Hans/Tutorials/images/bookstore-homepage.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png rename to docs/zh-Hans/Tutorials/images/bookstore-homepage.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v2.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png rename to docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v2.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-initial-book-list-page.png b/docs/zh-Hans/Tutorials/images/bookstore-initial-book-list-page.png new file mode 100644 index 0000000000..591cffb121 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-initial-book-list-page.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-initial-books-page-with-layout.png b/docs/zh-Hans/Tutorials/images/bookstore-initial-books-page-with-layout.png new file mode 100644 index 0000000000..629ad46444 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-initial-books-page-with-layout.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-localization-files-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-localization-files-v2.png new file mode 100644 index 0000000000..542cda209c Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-localization-files-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png b/docs/zh-Hans/Tutorials/images/bookstore-menu-items.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png rename to docs/zh-Hans/Tutorials/images/bookstore-menu-items.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-angular.png b/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-angular.png new file mode 100644 index 0000000000..0724e4ae8f Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-angular.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-mvc.png b/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-mvc.png new file mode 100644 index 0000000000..d59c0ce1d3 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-migrations-applied-mvc.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-new-book-button.png b/docs/zh-Hans/Tutorials/images/bookstore-new-book-button.png new file mode 100644 index 0000000000..8112fe1352 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-new-book-button.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-new-book-form-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-new-book-form-v2.png new file mode 100644 index 0000000000..72513de6e5 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-new-book-form-v2.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-new-book-form.png b/docs/zh-Hans/Tutorials/images/bookstore-new-book-form.png new file mode 100644 index 0000000000..95de64f8d4 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-new-book-form.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-new-menu-item.png b/docs/zh-Hans/Tutorials/images/bookstore-new-menu-item.png new file mode 100644 index 0000000000..97bf7fc7c1 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-new-menu-item.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-open-package-manager-console.png b/docs/zh-Hans/Tutorials/images/bookstore-open-package-manager-console.png new file mode 100644 index 0000000000..a640eb2681 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-open-package-manager-console.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration-v2.png new file mode 100644 index 0000000000..2baea20236 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png b/docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png rename to docs/zh-Hans/Tutorials/images/bookstore-pmc-add-book-migration.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-service-terminal-output.png b/docs/zh-Hans/Tutorials/images/bookstore-service-terminal-output.png new file mode 100644 index 0000000000..cf6145e03f Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-service-terminal-output.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-solution-structure-angular.png b/docs/zh-Hans/Tutorials/images/bookstore-solution-structure-angular.png new file mode 100644 index 0000000000..07d064a880 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-solution-structure-angular.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png b/docs/zh-Hans/Tutorials/images/bookstore-solution-structure-mvc.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png rename to docs/zh-Hans/Tutorials/images/bookstore-solution-structure-mvc.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-start-project-angular.png b/docs/zh-Hans/Tutorials/images/bookstore-start-project-angular.png new file mode 100644 index 0000000000..08abf845a8 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-start-project-angular.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-start-project-mvc.png b/docs/zh-Hans/Tutorials/images/bookstore-start-project-mvc.png new file mode 100644 index 0000000000..133dc6f131 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-start-project-mvc.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-swagger-book-dto-properties.png b/docs/zh-Hans/Tutorials/images/bookstore-swagger-book-dto-properties.png new file mode 100644 index 0000000000..66d630bb56 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-swagger-book-dto-properties.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-swagger.png b/docs/zh-Hans/Tutorials/images/bookstore-swagger.png new file mode 100644 index 0000000000..3ce36a11bc Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-swagger.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png b/docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist-network.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png rename to docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist-network.png diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png b/docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png rename to docs/zh-Hans/Tutorials/images/bookstore-test-js-proxy-getlist.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-test-projects-angular.png b/docs/zh-Hans/Tutorials/images/bookstore-test-projects-angular.png new file mode 100644 index 0000000000..6a8947238e Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-test-projects-angular.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-test-projects-mvc.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png rename to docs/zh-Hans/Tutorials/images/bookstore-test-projects-mvc.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-test-projects-v2.png b/docs/zh-Hans/Tutorials/images/bookstore-test-projects-v2.png new file mode 100644 index 0000000000..8701164d75 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-test-projects-v2.png differ diff --git a/docs/zh-Hans/Tutorials/images/bookstore-update-database-after-book-entity.png b/docs/zh-Hans/Tutorials/images/bookstore-update-database-after-book-entity.png new file mode 100644 index 0000000000..4889f4f757 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-update-database-after-book-entity.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png b/docs/zh-Hans/Tutorials/images/bookstore-user-management.png similarity index 100% rename from docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png rename to docs/zh-Hans/Tutorials/images/bookstore-user-management.png diff --git a/docs/zh-Hans/Tutorials/images/bookstore-visual-studio-solution-v3.png b/docs/zh-Hans/Tutorials/images/bookstore-visual-studio-solution-v3.png new file mode 100644 index 0000000000..307e3516a5 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/zh-Hans/Tutorials/images/generate-proxy-command.png b/docs/zh-Hans/Tutorials/images/generate-proxy-command.png new file mode 100644 index 0000000000..f850ce07a2 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/generate-proxy-command.png differ diff --git a/docs/zh-Hans/Tutorials/images/generated-proxies.png b/docs/zh-Hans/Tutorials/images/generated-proxies.png new file mode 100644 index 0000000000..9e466e7d55 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/generated-proxies.png differ diff --git a/docs/zh-Hans/Tutorials/images/mozilla-self-signed-cert-error.png b/docs/zh-Hans/Tutorials/images/mozilla-self-signed-cert-error.png new file mode 100644 index 0000000000..c9e2fc0e65 Binary files /dev/null and b/docs/zh-Hans/Tutorials/images/mozilla-self-signed-cert-error.png differ diff --git a/docs/zh-Hans/UI/Angular/Config-State.md b/docs/zh-Hans/UI/Angular/Config-State.md index 4967dd78b3..de645c8968 100644 --- a/docs/zh-Hans/UI/Angular/Config-State.md +++ b/docs/zh-Hans/UI/Angular/Config-State.md @@ -236,7 +236,7 @@ const newRoute: ABP.Route = { path: "page", invisible: false, order: 2, - requiredPolicy: "MyProjectName::MyNewPage" + requiredPolicy: "MyProjectName.MyNewPage" }; this.config.dispatchAddRoute(newRoute); @@ -248,16 +248,17 @@ this.config.dispatchAddRoute(newRoute); 如果你想要**添加一个子路由,你可以这样做:** ```js +import { eIdentityRouteNames } from '@abp/ng.identity'; // this.config is instance of ConfigStateService const newRoute: ABP.Route = { - parentName: "AbpAccount::Login", + parentName: eIdentityRouteNames.IdentityManagement, name: "My New Page", iconClass: "fa fa-dashboard", path: "page", invisible: false, order: 2, - requiredPolicy: "MyProjectName::MyNewPage" + requiredPolicy: "MyProjectName.MyNewPage" }; this.config.dispatchAddRoute(newRoute); @@ -291,4 +292,4 @@ this.config.dispatchSetEnvironment({ ## 下一步是什么? -* [组件替换](./Component-Replacement.md) \ No newline at end of file +- [修改菜单](./Modifying-the-Menu.md) \ No newline at end of file diff --git a/docs/zh-Hans/UI/Angular/Confirmation-Service.md b/docs/zh-Hans/UI/Angular/Confirmation-Service.md new file mode 100644 index 0000000000..a1a181837c --- /dev/null +++ b/docs/zh-Hans/UI/Angular/Confirmation-Service.md @@ -0,0 +1,181 @@ +# 确认弹层 + +你可以使用@abp/ng.theme.shared包中提供 `ConfirmationService` 放置在你项目的级别来显示确认弹层 + +## 入门 + +你不必在模块或组件级别提供 `ConfirmationService`,它已经在**根**级别提供,你可以在你的组件,指令或服务直接注入并使用它. + +```js +import { ConfirmationService } from '@abp/ng.theme.shared'; + +@Component({ + /* class metadata here */ +}) +class DemoComponent { + constructor(private confirmation: ConfirmationService) {} +} +``` + +## 用法 + +你可以使用 `ConfirmationService` 的 `success`, `warn`, `error` 和 `info` 方法显示一个确认弹层. + +### 如何显示一个确认弹层 + +```js +const confirmationStatus$ = this.confirmation.success('Message', 'Title'); +``` + +- `ConfirmationService` 方法接收三个参数,分别是 `message`, `title`, 和 `options`. +- `success`, `warn`, `error`, 和 `info` 方法返回一个[RxJS Subject](https://rxjs-dev.firebaseapp.com/guide/subject)监听确认弹层关闭事件. 事件值类型是 [`Confirmation.Status`](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts#L24)枚举. + +### 如何监听关闭事件 + +你可以订阅确认弹层关闭事件,例: + +```js +import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared'; + +constructor(private confirmation: ConfirmationService) {} + +this.confirmation + .warn('::WillBeDeleted', { key: '::AreYouSure', defaultValue: 'Are you sure?' }) + .subscribe((status: Confirmation.Status) => { + // your code here + }); +``` + +- `message` 和 `title` 参数接收字符串,本地化Key或本地化对象. 参阅[本地化文档](./Localization.md) +- `Confirmation.Status` 是一个枚举,具有三个属性; + - `Confirmation.Status.confirm` 是一个关闭事件值,当通过确认按钮关闭弹出窗口时触发此事件. + - `Confirmation.Status.reject` 是一个关闭事件值,当通过“取消”按钮关闭弹出窗口时触发此事件. + - `Confirmation.Status.dismiss` 是一个关闭事件值,当通过按Escape键关闭弹出窗口时触发此事件. + +如果你对确认状态不感兴趣,则不必订阅返回的observable: + +```js +this.confirmation.error('You are not authorized.', 'Error'); +``` + +### 如何显示具有给定选项的确认弹层 + +选项可以作为第三个参数传递给`success`, `warn`, `error`, 和 `info` 方法: + +```js +const options: Partial = { + hideCancelBtn: false, + hideYesBtn: false, + cancelText: 'Close', + yesText: 'Confirm', + messageLocalizationParams: ['Demo'], + titleLocalizationParams: [], +}; + +this.confirmation.warn( + 'AbpIdentity::RoleDeletionConfirmationMessage', + 'Are you sure?', + options, +); +``` + +- `hideCancelBtn` 选项为 `true` 时隐藏取消按钮. 默认值为 `false`. +- `hideYesBtn` 选项为 `true` 时隐藏确认按钮. 默认值为 `false`. +- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Cancel`. +- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`. +- `messageLocalizationParams`是用于消息本地化的插值参数. +- `titleLocalizationParams` 是标题本地化的插值参数. + +使用以上选项确认弹层窗口如下所示: + +![confirmation](./images/confirmation.png) + +你可以传递HTML字符串作为标题,消息或按钮文本. 例如: + +```js +const options: Partial = { + yesText: 'Yes, delete it', +}; + +this.confirmation.warn( + ` + Role Demo will be deleted +
+ Do you confirm that? + `, + 'Are you sure?', + options, +); +``` + +由于这些值现在是HTML,因此应该手动处理本地化. 参阅[LocalizationService](./Localization#using-the-localization-service)了解如何实现. + +> 注意,Angular会清除所有字符串,并且并非每个HTML字符串都可以使用. 仅显示被Angular视为"安全"的值. + +### 如何删除一个确认弹层 + +打开的确认弹出窗口可以通过 `clear` 方法手动删除: + +```js +this.confirmation.clear(); +``` + +## API + +### success + +```js +success( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +> 请参见[`Config.LocalizationParam`类型](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46)和[Confirmation名称空间](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts) + + +### warn + +```js +warn( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### error + +```js +error( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### info + +```js +info( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): Observable +``` + +### clear + +```js +clear( + status: Confirmation.Status = Confirmation.Status.dismiss +): void +``` + +- `status` 参数是确认关闭事件的值. + + +## 下一步是什么? + +- [Toast Overlay](./Toaster-Service.md) diff --git a/docs/zh-Hans/UI/Angular/Modifying-the-Menu.md b/docs/zh-Hans/UI/Angular/Modifying-the-Menu.md new file mode 100644 index 0000000000..ec3297ea78 --- /dev/null +++ b/docs/zh-Hans/UI/Angular/Modifying-the-Menu.md @@ -0,0 +1,197 @@ +# 修改菜单 + + +菜单在 @abp/ng.theme.basic包 `ApplicationLayoutComponent` 内部. 有几种修改菜单的方法,本文档介绍了这些方法. 如果你想完全替换菜单,请参考[组件替换文档]了解如何替换布局. + + + +## 如何添加Logo + +环境变量中的 `logoUrl` 是logo的url. + +你可以在 `src/assets` 文件夹下添加logo并设置 `logoUrl`: + +```js +export const environment = { + // other configurations + application: { + name: 'MyProjectName', + logoUrl: 'assets/logo.png', + }, + // other configurations +}; +``` + +## 如何添加导航元素 + +### 通过 AppRoutingModule 中的 `routes` 属性 + +你可以通过在 `app-routing.module` 中将路由作为子属性添加到路由配置的 `data` 属性来定义路由. `@abp/ng.core` 包组织路由并将其存储在 `ConfigState` 中.`ApplicationLayoutComponent` 从存储中获取路由显示在菜单上. + +你可以像以下一样添加 `routes` 属性: + +```js +{ + path: 'your-path', + data: { + routes: { + name: 'Your navigation', + order: 3, + iconClass: 'fas fa-question-circle', + requiredPolicy: 'permission key here', + children: [ + { + path: 'child', + name: 'Your child navigation', + order: 1, + requiredPolicy: 'permission key here', + }, + ], + } as ABP.Route, // can be imported from @abp/ng.core + } +} +``` + +- `name` 是导航元素的标签,可以传递本地化密钥或本地化对象. +- `order` 排序导航元素. +- `iconClass` 是 `i` 标签的类,在导航标签的左侧. +- `requiredPolicy` 是访问页面所需的权限key. 参阅 [权限管理文档](./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. +- `children` 是一个数组,用于声明子菜单,它基于给定的 `path` 属性,路径是在`/your-path/child`. + +添加了上面描述的route属性后,导航菜单如下图所示: + +![navigation-menu-via-app-routing](./images/navigation-menu-via-app-routing.png) + +## 通过 ConfigState + +`ConfigStateService` 的 `dispatchAddRoute` 方法可以向菜单添加一个新的导航元素. + +```js +// this.config is instance of ConfigStateService + +const newRoute: ABP.Route = { + name: 'My New Page', + iconClass: 'fa fa-dashboard', + path: 'page', + invisible: false, + order: 2, + requiredPolicy: 'MyProjectName.MyNewPage', +} as Omit; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +`newRoute` 放在根级别,没有任何父路由,url将为`/path`. + +如果你想 **添加子路由, 你可以这样做:** + +```js +// this.config is instance of ConfigStateService +// eIdentityRouteNames enum can be imported from @abp/ng.identity + +const newRoute: ABP.Route = { + parentName: eIdentityRouteNames.IdentityManagement, + name: 'My New Page', + iconClass: 'fa fa-dashboard', + path: 'page', + invisible: false, + order: 3, + requiredPolicy: 'MyProjectName.MyNewPage' +} as Omit; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +`newRoute` 做为 `eIdentityRouteNames.IdentityManagement` 的子路由添加, url 设置为 `'/identity/page'`. + +新路由看起来像这样: + +![navigation-menu-via-config-state](./images/navigation-menu-via-config-state.png) + +## 如何修改一个导航元素 + +`DispatchPatchRouteByName` 方法通过名称查找路由,并使用二个参数传递的新配置替换存储中的配置. + +```js +// this.config is instance of ConfigStateService +// eIdentityRouteNames enum can be imported from @abp/ng.identity + +const newRouteConfig: Partial = { + iconClass: 'fas fa-home', + parentName: eIdentityRouteNames.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 +``` + +* 根据给定的 `parentName` 将 _Home_ 导航移动到 _Administration_ 下拉框下. +* 添加了 icon. +* 指定了顺序. +* 添加了名为 _Dashboard_ 的子路由. + +修改后,导航元素看起来像这样: + +![navigation-menu-after-patching](./images/navigation-menu-after-patching.png) + +## 如何在菜单的右侧添加元素 + +右侧的元素存储在 @abp/ng.theme.basic 包的 `LayoutState` 中. + +`LayoutStateService` 的 `dispatchAddNavigationElement` 方法添加元素到右侧的菜单. + +你可以通过将模板添加到 `app.component` 调用 `dispatchAddNavigationElement` 方法来插入元素: + +```js +import { Layout, LayoutStateService } from '@abp/ng.theme.basic'; // added this line + +@Component({ + selector: 'app-root', + template: ` + + + + `, +}) +export class AppComponent { + // Added ViewChild + @ViewChild('search', { static: false, read: TemplateRef }) searchElementRef: TemplateRef; + + constructor(private layout: LayoutStateService) {} // injected LayoutStateService + + // Added ngAfterViewInit + ngAfterViewInit() { + const newElement = { + name: 'Search', + element: this.searchElementRef, + order: 1, + } as Layout.NavigationElement; + + this.layout.dispatchAddNavigationElement(newElement); + } +} +``` + +上面我们在菜单添加了一个搜索输入,最终UI如下:s + +![navigation-menu-search-input](./images/navigation-menu-search-input.png) + +## 如何删除右侧菜单元素 + +TODO + +## 下一步是什么? + +* [组件替换](./Component-Replacement.md) diff --git a/docs/zh-Hans/UI/Angular/Permission-Management.md b/docs/zh-Hans/UI/Angular/Permission-Management.md index 67ec1b787e..3a50da9591 100644 --- a/docs/zh-Hans/UI/Angular/Permission-Management.md +++ b/docs/zh-Hans/UI/Angular/Permission-Management.md @@ -76,4 +76,4 @@ const routes: Routes = [ ## 下一步是什么? -* [Config State](./Config-State.md) \ No newline at end of file +* [确认弹层](./Confirmation-Service.md) \ No newline at end of file diff --git a/docs/zh-Hans/UI/Angular/Projection-Strategy.md b/docs/zh-Hans/UI/Angular/Projection-Strategy.md index e69de29bb2..efcf8b4190 100644 --- a/docs/zh-Hans/UI/Angular/Projection-Strategy.md +++ b/docs/zh-Hans/UI/Angular/Projection-Strategy.md @@ -0,0 +1,179 @@ +# ProjectionStrategy + +`ProjectionStrategy` 是@abp/ng.core包暴露出的抽象类. 有三种扩展它的投影策略: `ComponentProjectionStrategy`, `RootComponentProjectionStrategy` 和 `TemplateProjectionStrategy`. 它们实现相同的方法和属性,均可以帮助你定义内容投影的工作方式. + +## ComponentProjectionStrategy + +`ComponentProjectionStrategy` 是扩展 `ProjectionStrategy` 的类. 它使你可以将**组件投影到容器中**. + +### constructor + +```js +constructor( + component: T, + private containerStrategy: ContainerStrategy, + private contextStrategy?: ContextStrategy, +) +``` + +- `component` 是你要投影的组件的类. +- `containerStrategy` 是在投影组件时将使用的 `ContainerStrategy`. +- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_) + +请参阅[ContainerStrategy](./Container-Strategy.md)和[ContextStrategy](./Context-Strategy.md)文档以了解其用法. + +### injectContent + +```js +injectContent(injector: Injector): ComponentRef +``` + +该方法准备容器,解析组件,设置其上下文并将其投影到容器中. 它返回一个 `ComponentRef` 实例,你应该保留该实例以便以后清除投影的组件. + +## RootComponentProjectionStrategy + +`RootComponentProjectionStrategy` 是扩展 `ProjectionStrategy` 的类. 它使你可以将**组件投影到文档中**,例如将其附加到 ``. + +### constructor + +```js +constructor( + component: T, + private contextStrategy?: ContextStrategy, + private domStrategy?: DomStrategy, +) +``` + +- `component` 是你要投影的组件的类. +- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_) +- `domStrategy` 是插入组件时将使用的 `DomStrategy`. (默认值: AppendToBody_) + +请参阅[ContextStrategy](./Context-Strategy.md)和[DomStrategy](./Dom-Strategy.md)文档以了解其用法. + +### injectContent + +```js +injectContent(injector: Injector): ComponentRef +``` + +该方法解析组件,设置其上下文并将其投影到文档中. 它返回一个 `ComponentRef` 实例,你应该保留该实例以便以后清除投影的组件. + +## TemplateProjectionStrategy + +`TemplateProjectionStrategy` 是扩展 `ProjectionStrategy` 的类.它使你可以将**模板投影到容器中**. + +### constructor + +```js +constructor( + template: T, + private containerStrategy: ContainerStrategy, + private contextStrategy?: ContextStrategy, +) +``` + +- `template` 是你要投影的 `TemplateRef`. +- `containerStrategy` 是在投影组件时将使用的 `ContainerStrategy`. +- `contextStrategy` 是将在投影组件上使用的 `ContextStrategy`. (默认值: None_) + +请参阅[ContainerStrategy](./Container-Strategy.md)和[ContextStrategy](./Context-Strategy.md)文档以了解其用法. + +### injectContent + +```js +injectContent(): EmbeddedViewRef +``` + +该方法准备容器,并将模板及其定义的上下文一起投影到容器. 它返回一个 `EmbeddedViewRef` 实例,你应该保留该实例以便以后清除投影的模板. + +## 预定义的投影策略 + +可以通过 `PROJECTION_STRATEGY` 常量访问预定义的投影策略. + +### AppendComponentToBody + +```js +PROJECTION_STRATEGY.AppendComponentToBody( + component: T, + contextStrategy?: ComponentContextStrategy, +) +``` + +将给定上下文设置到组件并将放置在文档中 `` 标签的**末尾**. + +### AppendComponentToContainer + +```js +PROJECTION_STRATEGY.AppendComponentToContainer( + component: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +将给定上下文设置到组件并将放置在容器的**末尾**. + +### AppendTemplateToContainer + +```js +PROJECTION_STRATEGY.AppendTemplateToContainer( + templateRef: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +将给定上下文设置到模板并将其放置在容器的**末尾**. + +### PrependComponentToContainer + +```js +PROJECTION_STRATEGY.PrependComponentToContainer( + component: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +将给定上下文设置到组件并将其放置在容器的**开头**. + +### PrependTemplateToContainer + +```js +PROJECTION_STRATEGY.PrependTemplateToContainer( + templateRef: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +将给定上下文设置到模板并将其放置在容器的**开头**. + + +### ProjectComponentToContainer + +```js +PROJECTION_STRATEGY.ProjectComponentToContainer( + component: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +清除容器,将给定的上下文设置到组件并放在**已清除**的容器中. + +### ProjectTemplateToContainer + +```js +PROJECTION_STRATEGY.ProjectTemplateToContainer( + templateRef: T, + containerRef: ViewContainerRef, + contextStrategy?: ComponentContextStrategy, +) +``` + +清除容器,将给定的上下文设置到模板并放在**已清除**的容器中. + +## 另请参阅 + +- [DomInsertionService](./Dom-Insertion-Service.md) diff --git a/docs/zh-Hans/UI/Angular/Service-Proxies.md b/docs/zh-Hans/UI/Angular/Service-Proxies.md index 5c7e334891..29aafebdae 100644 --- a/docs/zh-Hans/UI/Angular/Service-Proxies.md +++ b/docs/zh-Hans/UI/Angular/Service-Proxies.md @@ -1,3 +1,68 @@ ## 服务代理 -TODO... \ No newline at end of file +从Angular应用程序中调用服务器中的REST端点是很常见的, 在这种情况下我们通常创建**服务**(在服务器端具有针对每个服务方法的方法)和**模型对象**(与服务器端[DTO](../../Data-Transfer-Objects)匹配). + +除了手动创建这样的服务器交互服务之外,我们还可以使用[NSWAG](https://github.com/RicoSuter/NSwag)之类的工具来为我们生成服务代理. 但使用NSWAG过程中我们遇到以下问题: + +* 它生成一个**大的单个.ts文件**,该文件存在一些问题; + * 当你的应用程序增长时,它会变的**越来越大**. + * 它不适合ABP框架的[模块化](../../Module-Development-Basics)方法 +* 它创建了一些**难看的代码**. 我们希望有一个干净的代码(就像我们手动编写一样). +* 它不能生成在服务器端声明的相同**方法签名**(因为swagger.json并不完全反映后端服务的方法签名). 我们已经创建了一个端点公开了服务器端方法信息,以便客户端生成更好的一致的客户端代理. + +ABP CLI 的`generate-proxies` 命令在 `src/app` 文件夹中创建按模块名称分隔的文件夹,自动生成typescript客户端代理. + +在angular应用程序的**根文件夹**中运行以下命令: + +```bash +abp generate-proxy +``` + +它只为你自己的应用程序的服务创建代理. 不会为你正在使用的应用程序模块的服务创建代理(默认情况下). 有几个选项,参见[CLI文档](../../CLI). + +使用 `--module all` 选项生成的文件如下所示: + +![generated-files-via-generate-proxy](./images/generated-files-via-generate-proxy.png) + +### Services + +每个生成的服务都与后端控制器匹配. 服务方法通过[RestService](./Http-Requests#restservice)调用后端API. + +在每个服务中都定义了一个名为 `apiName` 的变量(自v2.4起可用). `apiName` 与模块的 `RemoteServiceName` 匹配. 在每次请求时该变量将作为参数传递给 `RestService`. 如果环境中未定义微服务API, `RestService` 使用默认值. 请参阅[从应用程序配置中获取特定的API端点](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config) + +服务的 `providerIn` 属性定义为 `'root'`. 因此无需将服务作为提供程序添加到模块. 你可以通过将服务注入到构造函数中来使用它,如下所示: + +```js +import { AbpApplicationConfigurationService } from '../app/shared/services'; + +//... +export class HomeComponent{ + constructor(private appConfigService: AbpApplicationConfigurationService) {} + + ngOnInit() { + this.appConfigService.get().subscribe() + } +} +``` + +Angular编译器会从最终输出中删除那些没有被注入的服务. 参见[摇树优化的提供者文档](https://angular.cn/guide/dependency-injection-providers#tree-shakable-providers). + +### Models + +生成的模型与后端中的dto匹配. 每个模型在 `src/app/*/shared/models` 文件夹生成一个类. + +`@abp/ng.core` 包有一些[基类](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/dtos.ts). 一些模型扩展了这些类. + +可以如下所示创建一个类的实例: + +```js +import { IdentityRoleCreateDto } from '../identity/shared/models'; +//... +const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, isPublic: true}) +``` + +可以选择将初始值传递给每个类构造函数. + +## 下一步是什么? + +* [HTTP请求](./Http-Requests) \ No newline at end of file diff --git a/docs/zh-Hans/UI/Angular/Toaster-Service.md b/docs/zh-Hans/UI/Angular/Toaster-Service.md new file mode 100644 index 0000000000..045cf9f2cf --- /dev/null +++ b/docs/zh-Hans/UI/Angular/Toaster-Service.md @@ -0,0 +1,155 @@ +# Toast Overlay + +你可以通常将@abp/ng.theme.shared包提供的 `ToasterService` 放置在你项目的根级别下以覆盖显示消息. + +## 入门 + +你不必在模块或组件级别提供 `ToasterService`,它已经在**根**级别提供,你可以在你的组件,指令或服务直接注入并使用它. + +```js +import { ToasterService } from '@abp/ng.theme.shared'; + +@Component({ + /* class metadata here */ +}) +class DemoComponent { + constructor(private toaster: ToasterService) {} +} +``` + +## 用法 + +你可以使用 `ToasterService` 的 `success`, `warn`, `error` 和 `info` 方法显示一个overlay. + +### 如何显示一个Toast Overlay + +```js +this.toast.success('Message', 'Title'); +``` + +- `ToasterService` 方法接收三个参数,分别是 `message`, `title`, 和 `options`. +- `success`, `warn`, `error`, 和 `info` 方法返回一个已打开的 toast overlay Id. 可以使用此id删除toast. + +### 如何显示具有给定选项的Toast Overlay + +选项可以作为第三个参数传递给`success`, `warn`, `error`, 和 `info` 方法: + +```js +import { Toaster, ToasterService } from '@abp/ng.theme.shared'; +//... + +constructor(private toaster: ToasterService) {} + +//... +const options: Partial = { + life: 10000, + sticky: false, + closable: true, + tapToDismiss: true, + messageLocalizationParams: ['Demo', '1'], + titleLocalizationParams: [] + }; + + this.toaster.error('AbpUi::EntityNotFoundErrorMessage', 'AbpUi::Error', options); +``` + +- `life` 选项是关闭的时间毫秒数. 默认值是 `5000`. +- `sticky` 选项为 `true` 时忽略 `life` 选项,将toast overlay留在屏幕上. 默认值是 `false`. +- `closable` 选项为 `true` 时在toast overlay上显示关闭图标. 默认值是 `true`. +- `tapToDismiss` 选项为 `true` 允许通过单击关闭toast overlay. 默认值是 `false`. +- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`. +- `messageLocalizationParams` 是用于消息本地化的插值参数. +- `titleLocalizationParams` 是标题本地化的插值参数. + +使用上面的选项,toast overlay看起来像这样: + +![toast](./images/toast.png) + +### 如何删除 Toast Overlay + +已打开的toast overlay可以通过手动调用 `remove` 方法传递指定的 toast `id`删除. + +```js +const toastId = this.toast.success('Message', 'Title') + +this.toast.remove(toastId); +``` + +### 如何删除所有的Toasts + +可以手动调用 `clear` 方法删除所有的已打开的toasts. + +```js +this.toast.clear(); +``` + +## API + +### success + +```js +success( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +- `Config` 命令空间可以从 `@abp/ng.core` 导入. +- `Toaster` 命令空间可以从 `@abp/ng.theme.shared` 导入. + +> 请参见[`Config.LocalizationParam`类型](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46)和[`Toaster` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts) + +### warn + +```js +warn( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### error + +```js +error( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### info + +```js +info( + message: Config.LocalizationParam, + title: Config.LocalizationParam, + options?: Partial, +): number +``` + +### remove + +```js +remove(id: number): void +``` + +按给定的id移除打开的toast. + +### clear + +```js +clear(): void +``` + +删除所有打开的toasts. + +## 另请参阅 + +- [Confirmation Popup](./Confirmation-Service.md) + +## 确认弹层? + +- [Config State](./Config-State.md) diff --git a/docs/zh-Hans/UI/Angular/images/confirmation.png b/docs/zh-Hans/UI/Angular/images/confirmation.png new file mode 100644 index 0000000000..efe4c98ea7 Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/confirmation.png differ diff --git a/docs/zh-Hans/UI/Angular/images/custom-settings.png b/docs/zh-Hans/UI/Angular/images/custom-settings.png index 32d7516849..05dd3f695a 100644 Binary files a/docs/zh-Hans/UI/Angular/images/custom-settings.png and b/docs/zh-Hans/UI/Angular/images/custom-settings.png differ diff --git a/docs/zh-Hans/UI/Angular/images/generated-files-via-generate-proxy.png b/docs/zh-Hans/UI/Angular/images/generated-files-via-generate-proxy.png new file mode 100644 index 0000000000..77a120a35d Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/generated-files-via-generate-proxy.png differ diff --git a/docs/zh-Hans/UI/Angular/images/navigation-menu-after-patching.png b/docs/zh-Hans/UI/Angular/images/navigation-menu-after-patching.png new file mode 100644 index 0000000000..2f6bf08c88 Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/navigation-menu-after-patching.png differ diff --git a/docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png b/docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png new file mode 100644 index 0000000000..ebdc05e3e0 Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png differ diff --git a/docs/zh-Hans/UI/Angular/images/navigation-menu-via-app-routing.png b/docs/zh-Hans/UI/Angular/images/navigation-menu-via-app-routing.png new file mode 100644 index 0000000000..4d5f61301c Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/navigation-menu-via-app-routing.png differ diff --git a/docs/zh-Hans/UI/Angular/images/navigation-menu-via-config-state.png b/docs/zh-Hans/UI/Angular/images/navigation-menu-via-config-state.png new file mode 100644 index 0000000000..19944f154d Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/navigation-menu-via-config-state.png differ diff --git a/docs/zh-Hans/UI/Angular/images/toast.png b/docs/zh-Hans/UI/Angular/images/toast.png new file mode 100644 index 0000000000..24cdd0fe0c Binary files /dev/null and b/docs/zh-Hans/UI/Angular/images/toast.png differ diff --git a/docs/zh-Hans/UI/Common/Utils/Linked-List.md b/docs/zh-Hans/UI/Common/Utils/Linked-List.md index 871db960ab..53c356628f 100644 --- a/docs/zh-Hans/UI/Common/Utils/Linked-List.md +++ b/docs/zh-Hans/UI/Common/Utils/Linked-List.md @@ -1229,7 +1229,7 @@ find(predicate: ListIteratorFunction): ListNode | undefined 从链表中找到与给定谓词匹配的第一个节点: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1251,7 +1251,7 @@ findIndex(predicate: ListIteratorFunction): number 从链表中找到与给定谓词匹配的第一个节点的位置: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1279,7 +1279,7 @@ get(position: number): ListNode | undefined 查找并返回链表中特定位置的节点: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1303,7 +1303,7 @@ indexOf(value: T, compareFn?: ListComparisonFn): number 在链表中找到匹配给定值的第一个节点位置: ```js -list.addTailMany(['a', 'b', 'b', 'c']); +list.addManyTail(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -1325,7 +1325,7 @@ i3 === -1 你可以自定义比较器 ```js -list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); +list.addManyTail([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -1365,7 +1365,7 @@ forEach(iteratorFn: ListIteratorFn): void 从头到尾在链表中的所有节点上运行回调函数: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1381,7 +1381,7 @@ list.forEach((node, index) => console.log(node.value + index)); 链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法. ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1405,7 +1405,7 @@ toArray(): T[] 转换链表值为数组: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1427,7 +1427,7 @@ toNodeArray(): T[] 转换链表节点为数组: ```js -list.addTailMany(['a', 'b', 'c']); +list.addManyTail(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -1449,7 +1449,7 @@ toString(mapperFn: ListMapperFn = JSON.stringify): string 将链表转换为节点及其关系的字符串表示形式: ```js -list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); +list.addManyTail(['a', 2, 'c', { k: 4, v: 'd' }]); // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} @@ -1507,7 +1507,7 @@ export class ListNode { - `previous`引用列表中的上一个节点. ```js -list.addTailMany([ 0, 1, 2 ]); +list.addManyTail([ 0, 1, 2 ]); console.log( list.head.value, // 0 diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 384c406475..c70284637d 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -5,13 +5,7 @@ "items": [ { "text": "从启动模板开始", - "path": "Getting-Started-With-Startup-Templates.md", - "items": [ - { - "text": "ASP.NET Core MVC 模板", - "path": "Getting-Started-AspNetCore-MVC-Template.md" - } - ] + "path": "Getting-Started.md" }, { "text": "从空项目开始", @@ -218,8 +212,11 @@ }, { "text": "领域驱动设计", - "path": "Domain-Driven-Design.md", "items": [ + { + "text": "概述", + "path": "Domain-Driven-Design.md" + }, { "text": "领域层", "items": [ @@ -324,10 +321,22 @@ "text": "权限管理", "path": "UI/Angular/Permission-Management.md" }, + { + "text": "确认弹层", + "path": "UI/Angular/Confirmation-Service.md" + }, + { + "text": "Toast Overlay", + "path": "UI/Angular/Toaster-Service.md" + }, { "text": "配置状态", "path": "UI/Angular/Config-State.md" }, + { + "text": "修改菜单", + "path": "UI/Angular/Modifying-the-Menu.md" + }, { "text": "替换组件", "path": "UI/Angular/Component-Replacement.md" @@ -372,8 +381,11 @@ }, { "text": "数据访问", - "path":"Data-Access.md", "items": [ + { + "text": "概述", + "path": "Data-Access.md" + }, { "text": "连接字符串", "path": "Connection-Strings.md" @@ -457,8 +469,11 @@ }, { "text": "启动模板", - "path": "Startup-Templates/Index.md", "items": [ + { + "text": "概述", + "path": "Startup-Templates/Index.md" + }, { "text": "应用程序", "path": "Startup-Templates/Application.md" diff --git a/docs/zh-Hans/docs-params.json b/docs/zh-Hans/docs-params.json new file mode 100644 index 0000000000..23d079f9bb --- /dev/null +++ b/docs/zh-Hans/docs-params.json @@ -0,0 +1,28 @@ +{ + "parameters": [ + { + "name": "UI", + "displayName": "UI", + "values": { + "MVC": "MVC / Razor Pages", + "NG": "Angular" + } + }, + { + "name": "DB", + "displayName": "Database", + "values": { + "EF": "Entity Framework Core", + "Mongo": "MongoDB" + } + }, + { + "name": "Tiered", + "displayName": "Tiered", + "values": { + "No": "Not Tiered", + "Yes": "Tiered" + } + } + ] +} \ No newline at end of file diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 3db0998855..5159f0a86e 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -277,7 +277,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.Identi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending", "src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj", "{D1815C77-16D6-4F99-8814-69065CD89FB3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating", "src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj", "{9E53F91F-EACD-4191-A487-E727741F1311}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -829,6 +833,14 @@ Global {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.Build.0 = Release|Any CPU + {9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.Build.0 = Release|Any CPU + {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.Build.0 = Debug|Any CPU + {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.ActiveCfg = Release|Any CPU + {251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -970,6 +982,8 @@ Global {E1963439-2BE5-4DB5-8438-2A9A792A1ADA} = {447C8A77-E5F0-4538-8687-7383196D04EA} {D1815C77-16D6-4F99-8814-69065CD89FB3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {9E53F91F-EACD-4191-A487-E727741F1311} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index 6f8ccf285a..a1601378d9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -43,6 +43,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form await ProcessFieldsAsync(context, output); + RemoveFormGroupItemsNotInModel(context, output, list); + SetContent(context, output, list, childContent); SetFormAttributes(context, output); @@ -143,6 +145,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form } } + + protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List items) + { + var models = GetModels(context, output); + + items.RemoveAll(x => models.All(m => !m.Name.Equals(x.PropertyName, StringComparison.InvariantCultureIgnoreCase))); + } + protected virtual async Task ProcessSelectGroupAsync(TagHelperContext context, TagHelperOutput output, ModelExpression model) { var abpSelectTagHelper = GetSelectGroupTagHelper(context, output, model); @@ -152,7 +162,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual AbpTagHelper GetSelectGroupTagHelper(TagHelperContext context, TagHelperOutput output, ModelExpression model) { - return IsRadioGroup(model.ModelExplorer) ? + return IsRadioGroup(model.ModelExplorer) ? GetAbpRadioInputTagHelper(model) : GetSelectTagHelper(model); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index 75fe4f23e7..470b60004e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -58,7 +58,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual async Task<(string, bool)> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output) { var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output); - + var inputHtml = inputTag.Render(_encoder); var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox); var info = GetInfoAsHtml(context, output, inputTag, isCheckBox); @@ -161,7 +161,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual void AddDisabledAttribute(TagHelperOutput inputTagHelperOutput) { - if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false && + if (inputTagHelperOutput.Attributes.ContainsName("disabled") == false && (TagHelper.IsDisabled || TagHelper.AspFor.ModelExplorer.GetAttribute() != null)) { inputTagHelperOutput.Attributes.Add("disabled", ""); @@ -170,7 +170,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput) { - if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false && + if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false && (TagHelper.IsReadonly != false || TagHelper.AspFor.ModelExplorer.GetAttribute() != null)) { inputTagHelperOutput.Attributes.Add("readonly", ""); @@ -400,9 +400,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form list.Add(new FormGroupItem { HtmlContent = html, - Order = order + Order = order, + PropertyName = propertyName }); } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs index 6f71842b61..010cc1d075 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs @@ -167,9 +167,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form list.Add(new FormGroupItem { HtmlContent = html, - Order = order + Order = order, + PropertyName = propertyName }); } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs index b18bc6ed75..3ed4f63f82 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -125,7 +125,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return await GetLabelAsHtmlUsingTagHelperAsync(context, output) + GetRequiredSymbol(context, output); } - + protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output) { if (!TagHelper.DisplayRequiredSymbol) @@ -221,7 +221,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form } protected virtual List GetSelectItemsFromAttribute( - SelectItems selectItemsAttribute, + SelectItems selectItemsAttribute, ModelExplorer explorer) { var selectItems = selectItemsAttribute.GetItems(explorer)?.ToList(); @@ -329,9 +329,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form list.Add(new FormGroupItem { HtmlContent = html, - Order = order + Order = order, + PropertyName = propertyName }); } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupItem.cs index 258ba544f8..e3d33a4359 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupItem.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormGroupItem.cs @@ -5,5 +5,7 @@ public string HtmlContent { get; set; } public int Order { get; set; } + + public string PropertyName { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json similarity index 92% rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl.json rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json index 8509b7d02b..438d770a7c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}", "Tenant": "Tenant", diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index e6a2319c72..ca8bfa9bf8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -57,7 +57,11 @@ namespace Volo.Abp.Cli.Commands .Options .GetOrNull(Options.IncludePreviews.Short, Options.IncludePreviews.Long) != null; - var solution = Directory.GetFiles(directory, "*.sln", SearchOption.AllDirectories).FirstOrDefault(); + var solution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); + if (solution.IsNullOrWhiteSpace()) + { + solution = Directory.GetFiles(directory, "*.sln", SearchOption.AllDirectories).FirstOrDefault(); + } if (solution != null) { @@ -101,11 +105,14 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine("-p|--include-previews (if supported by the template)"); sb.AppendLine("--npm (Only updates NPM packages)"); sb.AppendLine("--nuget (Only updates Nuget packages)"); + sb.AppendLine("-sp|--solution-path (Specify the solution path)"); + sb.AppendLine("-sn|--solution-name (Specify the solution name)"); sb.AppendLine(""); sb.AppendLine("Some examples:"); sb.AppendLine(""); sb.AppendLine(" abp update"); sb.AppendLine(" abp update -p"); + sb.AppendLine(" abp update -sp \"D:\\projects\\\" -sn Acme.BookStore"); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); @@ -125,6 +132,12 @@ namespace Volo.Abp.Cli.Commands public const string Long = "solution-path"; } + public static class SolutionName + { + public const string Short = "sn"; + public const string Long = "solution-name"; + } + public static class IncludePreviews { public const string Short = "p"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs index ca1a23fcfc..bd3037d830 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs @@ -1,9 +1,15 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Text; +using System.Threading; +using System.Threading.Tasks; using IdentityModel.Client; +using Polly; +using Polly.Extensions.Http; using Volo.Abp.Cli.Auth; +using Microsoft.Extensions.Logging; namespace Volo.Abp.Cli.Http { @@ -41,5 +47,55 @@ namespace Volo.Abp.Cli.Http client.SetBearerToken(accessToken); } } + + public async Task GetHttpResponseMessageWithRetryAsync + ( + string url, + CancellationToken? cancellationToken = null, + ILogger logger = null, + IEnumerable sleepDurations = null + ) + { + if (sleepDurations == null) + { + sleepDurations = new[] + { + TimeSpan.FromSeconds(2), + TimeSpan.FromSeconds(4), + TimeSpan.FromSeconds(7) + }; + } + + if (!cancellationToken.HasValue) + { + cancellationToken = CancellationToken.None; + } + + return await HttpPolicyExtensions + .HandleTransientHttpError() + .OrResult(msg => !msg.IsSuccessStatusCode) + .WaitAndRetryAsync(sleepDurations, + (responseMessage, timeSpan, retryCount, context) => + { + if (responseMessage.Exception != null) + { + string httpErrorCode = responseMessage.Result == null ? + httpErrorCode = string.Empty : + "HTTP-" + (int)responseMessage.Result.StatusCode + ", "; + + logger?.LogWarning( + $"{retryCount}. HTTP request attempt failed to {url} with an error: {httpErrorCode}{responseMessage.Exception.Message}. " + + $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); + } + else if (responseMessage.Result != null) + { + logger?.LogWarning( + $"{retryCount}. HTTP request attempt failed to {url} with an error: {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + + $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); + } + }) + .ExecuteAsync(async () => await this.GetAsync(url, cancellationToken.Value)); + } + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs index cddead548f..33a2e10fdf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs @@ -13,6 +13,7 @@ using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.DependencyInjection; using Volo.Abp.Json; +using Volo.Abp.Threading; namespace Volo.Abp.Cli.Licensing { @@ -20,14 +21,21 @@ namespace Volo.Abp.Cli.Licensing { protected IJsonSerializer JsonSerializer { get; } protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + private readonly ILogger _logger; private DeveloperApiKeyResult _apiKeyResult = null; - public AbpIoApiKeyService(IJsonSerializer jsonSerializer, IRemoteServiceExceptionHandler remoteServiceExceptionHandler, ILogger logger) + public AbpIoApiKeyService( + IJsonSerializer jsonSerializer, + ICancellationTokenProvider cancellationTokenProvider, + IRemoteServiceExceptionHandler remoteServiceExceptionHandler, + ILogger logger) { JsonSerializer = jsonSerializer; RemoteServiceExceptionHandler = remoteServiceExceptionHandler; _logger = logger; + CancellationTokenProvider = cancellationTokenProvider; } public async Task GetApiKeyOrNullAsync(bool invalidateCache = false) @@ -51,31 +59,10 @@ namespace Volo.Abp.Cli.Licensing using (var client = new CliHttpClient()) { - var response = await HttpPolicyExtensions - .HandleTransientHttpError() - .OrResult(msg => !msg.IsSuccessStatusCode) - .WaitAndRetryAsync(new[] - { - TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(7) - }, - (responseMessage, timeSpan, retryCount, context) => - { - if (responseMessage.Exception != null) - { - _logger.LogWarning( - $"{retryCount}. request attempt failed to {url} with an error: \"{responseMessage.Exception.Message}\". " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - else if (responseMessage.Result != null) - { - _logger.LogWarning( - $"{retryCount}. request attempt failed {url} with {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - }) - .ExecuteAsync(async () => await client.GetAsync(url)); + var response = await client.GetHttpResponseMessageWithRetryAsync( + url: url, + cancellationToken: CancellationTokenProvider.Token, + logger: _logger); if (!response.IsSuccessStatusCode) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs index c0eb6fd209..0fd35582bf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs @@ -1,16 +1,10 @@ using Newtonsoft.Json; using NuGet.Versioning; -using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Polly; -using Polly.Extensions.Http; using Volo.Abp.Cli.Auth; using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Licensing; @@ -29,6 +23,8 @@ namespace Volo.Abp.Cli.NuGet protected ICancellationTokenProvider CancellationTokenProvider { get; } protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } private readonly IApiKeyService _apiKeyService; + private List _proPackageList; + private DeveloperApiKeyResult _apiKeyResult; public NuGetService( IJsonSerializer jsonSerializer, @@ -45,20 +41,20 @@ namespace Volo.Abp.Cli.NuGet public async Task GetLatestVersionOrNullAsync(string packageId, bool includePreviews = false, bool includeNightly = false) { - List proPackageList = null; - if (AuthService.IsLoggedIn()) { - proPackageList = await GetProPackageListAsync(); + if (_proPackageList == null) + { + _proPackageList = await GetProPackageListAsync(); + } } string url; if (includeNightly) { - url = - $"https://www.myget.org/F/abp-nightly/api/v3/flatcontainer/{packageId.ToLowerInvariant()}/index.json"; + url = $"https://www.myget.org/F/abp-nightly/api/v3/flatcontainer/{packageId.ToLowerInvariant()}/index.json"; } - else if (proPackageList?.Contains(packageId) ?? false) + else if (_proPackageList?.Contains(packageId) ?? false) { url = await GetNuGetUrlForCommercialPackage(packageId); } @@ -67,15 +63,13 @@ namespace Volo.Abp.Cli.NuGet url = $"https://api.nuget.org/v3-flatcontainer/{packageId.ToLowerInvariant()}/index.json"; } - using (var client = new CliHttpClient(setBearerToken: false)) { - var responseMessage = await GetHttpResponseMessageWithRetryAsync(client, url); - - if (!responseMessage.IsSuccessStatusCode) - { - throw new Exception($"ERROR: Remote server returns '{responseMessage.StatusCode}'"); - } + var responseMessage = await client.GetHttpResponseMessageWithRetryAsync( + url, + cancellationToken: CancellationTokenProvider.Token, + logger: Logger + ); await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(responseMessage); @@ -98,62 +92,41 @@ namespace Volo.Abp.Cli.NuGet private async Task GetNuGetUrlForCommercialPackage(string packageId) { - var apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync(); - return CliUrls.GetNuGetPackageInfoUrl(apiKeyResult.ApiKey, packageId); - } + if (_apiKeyResult == null) + { + _apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync(); + } - private async Task GetHttpResponseMessageWithRetryAsync(HttpClient client, string url) - { - return await HttpPolicyExtensions - .HandleTransientHttpError() - .OrResult(msg => !msg.IsSuccessStatusCode) - .WaitAndRetryAsync(new[] - { - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(4), - TimeSpan.FromSeconds(7) - }, - (responseMessage, timeSpan, retryCount, context) => - { - if (responseMessage.Exception != null) - { - Logger.LogWarning( - $"{retryCount}. HTTP request attempt failed to {url} with an error: HTTP {(int)responseMessage.Result.StatusCode}-{responseMessage.Exception.Message}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - else if (responseMessage.Result != null) - { - Logger.LogWarning( - $"{retryCount}. HTTP request attempt failed to {url} with an error: {(int)responseMessage.Result.StatusCode}-{responseMessage.Result.ReasonPhrase}. " + - $"Waiting {timeSpan.TotalSeconds} secs for the next try..."); - } - }) - .ExecuteAsync(async () => await client.GetAsync(url, CancellationTokenProvider.Token)); + return CliUrls.GetNuGetPackageInfoUrl(_apiKeyResult.ApiKey, packageId); } private async Task> GetProPackageListAsync() { using var client = new CliHttpClient(); - var responseMessage = await client.GetAsync( - $"{CliUrls.WwwAbpIo}api/app/nugetPackage/proPackageNames", - CancellationTokenProvider.Token + var url = $"{CliUrls.WwwAbpIo}api/app/nugetPackage/proPackageNames"; + + var responseMessage = await client.GetHttpResponseMessageWithRetryAsync( + url: url, + cancellationToken: CancellationTokenProvider.Token, + logger: Logger ); - if (!responseMessage.IsSuccessStatusCode) + if (responseMessage.IsSuccessStatusCode) { - var exceptionMessage = "Remote server returns '" + (int)responseMessage.StatusCode + "-" + responseMessage.ReasonPhrase + "'. "; - var remoteServiceErrorMessage = await RemoteServiceExceptionHandler.GetAbpRemoteServiceErrorAsync(responseMessage); + return JsonSerializer.Deserialize>(await responseMessage.Content.ReadAsStringAsync()); + } - if (remoteServiceErrorMessage != null) - { - exceptionMessage += remoteServiceErrorMessage; - } - Logger.LogInformation(exceptionMessage); - return null; + var exceptionMessage = "Remote server returns '" + (int)responseMessage.StatusCode + "-" + responseMessage.ReasonPhrase + "'. "; + var remoteServiceErrorMessage = await RemoteServiceExceptionHandler.GetAbpRemoteServiceErrorAsync(responseMessage); + + if (remoteServiceErrorMessage != null) + { + exceptionMessage += remoteServiceErrorMessage; } - return JsonSerializer.Deserialize>(await responseMessage.Content.ReadAsStringAsync()); + Logger.LogError(exceptionMessage); + return null; } public class NuGetVersionResultDto diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs index 41907276f4..75e9156ce0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.Cli.ProjectModification Logger = NullLogger.Instance; } - public async Task GetPackages() + public async Task GetPackagesAsync() { if (_response != null) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index a5a2e506cc..1a279fafc7 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -13,25 +13,31 @@ using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.IO; +using Volo.Abp.Threading; namespace Volo.Abp.Cli.ProjectModification { public class NpmPackagesUpdater : ITransientDependency { public ILogger Logger { get; set; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } private readonly PackageJsonFileFinder _packageJsonFileFinder; private readonly NpmGlobalPackagesChecker _npmGlobalPackagesChecker; private readonly MyGetPackageListFinder _myGetPackageListFinder; - private readonly Dictionary _fileVersionStorage = new Dictionary(); + private MyGetApiResponse _myGetApiResponse; - public NpmPackagesUpdater(PackageJsonFileFinder packageJsonFileFinder, NpmGlobalPackagesChecker npmGlobalPackagesChecker, MyGetPackageListFinder myGetPackageListFinder) + public NpmPackagesUpdater( + PackageJsonFileFinder packageJsonFileFinder, + NpmGlobalPackagesChecker npmGlobalPackagesChecker, + MyGetPackageListFinder myGetPackageListFinder, + ICancellationTokenProvider cancellationTokenProvider) { _packageJsonFileFinder = packageJsonFileFinder; _npmGlobalPackagesChecker = npmGlobalPackagesChecker; _myGetPackageListFinder = myGetPackageListFinder; - + CancellationTokenProvider = cancellationTokenProvider; Logger = NullLogger.Instance; } @@ -77,7 +83,7 @@ namespace Volo.Abp.Cli.ProjectModification } } - private async Task DeleteNpmrcFileAsync(string directoryName) + private static async Task DeleteNpmrcFileAsync(string directoryName) { FileHelper.DeleteIfExists(Path.Combine(directoryName, ".npmrc")); @@ -135,11 +141,13 @@ namespace Volo.Abp.Cli.ProjectModification { using (var client = new CliHttpClient(TimeSpan.FromMinutes(1))) { - var responseMessage = await client.GetAsync( - $"{CliUrls.WwwAbpIo}api/myget/apikey/" + var response = await client.GetHttpResponseMessageWithRetryAsync( + url: $"{CliUrls.WwwAbpIo}api/myget/apikey/", + cancellationToken: CancellationTokenProvider.Token, + logger: Logger ); - return Encoding.Default.GetString(await responseMessage.Content.ReadAsByteArrayAsync()); + return Encoding.Default.GetString(await response.Content.ReadAsByteArrayAsync()); } } catch (Exception) @@ -148,26 +156,26 @@ namespace Volo.Abp.Cli.ProjectModification } } - private bool IsAngularProject(string fileDirectory) + private static bool IsAngularProject(string fileDirectory) { return File.Exists(Path.Combine(fileDirectory, "angular.json")); } - protected virtual async Task UpdatePackagesInFile(string file, bool includePreviews = false, bool switchToStable = false) + protected virtual async Task UpdatePackagesInFile(string filePath, bool includePreviews = false, bool switchToStable = false) { var packagesUpdated = false; - var fileContent = File.ReadAllText(file); + var fileContent = File.ReadAllText(filePath); var packageJson = JObject.Parse(fileContent); var abpPackages = GetAbpPackagesFromPackageJson(packageJson); if (!abpPackages.Any()) { - return packagesUpdated; + return false; } foreach (var abpPackage in abpPackages) { - var updated = await TryUpdatePackage(file, abpPackage, includePreviews, switchToStable); + var updated = await TryUpdatingPackage(filePath, abpPackage, includePreviews, switchToStable); if (updated) { @@ -175,15 +183,18 @@ namespace Volo.Abp.Cli.ProjectModification } } - var modifiedFileContent = packageJson.ToString(Formatting.Indented); + var updatedContent = packageJson.ToString(Formatting.Indented); - File.WriteAllText(file, modifiedFileContent); + File.WriteAllText(filePath, updatedContent); return packagesUpdated; } - protected virtual async Task TryUpdatePackage(string file, JProperty package, - bool includePreviews = false, bool switchToStable = false) + protected virtual async Task TryUpdatingPackage( + string filePath, + JProperty package, + bool includePreviews = false, + bool switchToStable = false) { var currentVersion = (string)package.Value; @@ -198,23 +209,31 @@ namespace Volo.Abp.Cli.ProjectModification package.Value.Replace(versionWithPrefix); - Logger.LogInformation($"Updated {package.Name} to {version} in {file.Replace(Directory.GetCurrentDirectory(), "")}."); + Logger.LogInformation($"Updated {package.Name} to {version} in {filePath.Replace(Directory.GetCurrentDirectory(), "")}."); return true; } - protected virtual async Task GetLatestVersion(JProperty package, string currentVersion, - bool includePreviews = false, bool switchToStable = false) + protected virtual async Task GetLatestVersion( + JProperty package, + string currentVersion, + bool includePreviews = false, + bool switchToStable = false) { if (_fileVersionStorage.ContainsKey(package.Name)) { return _fileVersionStorage[package.Name]; } - string newVersion = currentVersion; + var newVersion = currentVersion; if (includePreviews || (!switchToStable && currentVersion.Contains("-preview"))) { - var mygetPackage = (await _myGetPackageListFinder.GetPackages()).Packages.FirstOrDefault(p => p.Id == package.Name); + if (_myGetApiResponse == null) + { + _myGetApiResponse = await _myGetPackageListFinder.GetPackagesAsync(); + } + + var mygetPackage = _myGetApiResponse.Packages.FirstOrDefault(p => p.Id == package.Name); if (mygetPackage != null) { newVersion = mygetPackage.Versions.Last(); @@ -225,7 +244,6 @@ namespace Volo.Abp.Cli.ProjectModification newVersion = CmdHelper.RunCmdAndGetOutput($"npm show {package.Name} version"); } - _fileVersionStorage[package.Name] = newVersion; return newVersion; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 587492804b..fb2a386ef9 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -42,8 +42,9 @@ namespace Volo.Abp.Cli.ProjectModification protected virtual async Task UpdateInternalAsync(string projectPath, bool includePreviews = false, bool switchToStable = false) { var fileContent = File.ReadAllText(projectPath); + var updatedContent = await UpdateVoloPackagesAsync(fileContent, includePreviews, switchToStable); - File.WriteAllText(projectPath, await UpdateVoloPackagesAsync(fileContent, includePreviews, switchToStable)); + File.WriteAllText(projectPath, updatedContent); } private async Task UpdateVoloPackagesAsync(string content, bool includePreviews = false, bool switchToStable = false) @@ -74,16 +75,13 @@ namespace Volo.Abp.Cli.ProjectModification var versionAttribute = package.Attributes["Version"]; var currentVersion = versionAttribute.Value; - var packageVersion = SemanticVersion.Parse(currentVersion); - - Logger.LogDebug("Checking package: \"{0}\" - Current version: {1}", packageId, packageVersion); + var currentSemanticVersion = SemanticVersion.Parse(currentVersion); + Logger.LogDebug("Checking package: \"{0}\" - Current version: {1}", packageId, currentSemanticVersion); if (includePreviews || (currentVersion.Contains("-preview") && !switchToStable)) { - var latestVersion = (await _myGetPackageListFinder.GetPackages()).Packages - .FirstOrDefault(p => p.Id == packageId) - ?.Versions.LastOrDefault(); + var latestVersion = await GetLatestVersionFromMyGet(packageId); if (currentVersion != latestVersion) { @@ -99,14 +97,14 @@ namespace Volo.Abp.Cli.ProjectModification { var latestVersion = await _nuGetService.GetLatestVersionOrNullAsync(packageId); - if (latestVersion != null && (currentVersion.Contains("-preview") || packageVersion < latestVersion)) + if (latestVersion != null && (currentVersion.Contains("-preview") || currentSemanticVersion < latestVersion)) { - Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, packageVersion.ToString(), latestVersion.ToString()); + Logger.LogInformation("Updating package \"{0}\" from v{1} to v{2}.", packageId, currentSemanticVersion.ToString(), latestVersion.ToString()); versionAttribute.Value = latestVersion.ToString(); } else { - Logger.LogInformation("Package: \"{0}-v{1}\" is up to date.", packageId, packageVersion); + Logger.LogInformation("Package: \"{0}-v{1}\" is up to date.", packageId, currentSemanticVersion); } } } @@ -116,11 +114,18 @@ namespace Volo.Abp.Cli.ProjectModification } catch (Exception ex) { - Logger.LogError("Cannot update volo packages! An error occured while updating the package \"{0}\". Error: {1}", packageId, ex.Message); + Logger.LogError("Cannot update Volo.* packages! An error occured while updating the package \"{0}\". Error: {1}", packageId, ex.Message); Logger.LogException(ex); } return await Task.FromResult(content); } + + private async Task GetLatestVersionFromMyGet(string packageId) + { + var myGetPack = await _myGetPackageListFinder.GetPackagesAsync(); + + return myGetPack.Packages.FirstOrDefault(p => p.Id == packageId)?.Versions.LastOrDefault(); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs index 92320f737c..d4d9cfb1c0 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs @@ -10,7 +10,12 @@ namespace Volo.Abp.Localization { Check.NotNull(culture, nameof(culture)); - return Use(new CultureInfo(culture), uiCulture == null ? null : new CultureInfo(uiCulture)); + return Use( + new CultureInfo(culture), + uiCulture == null + ? null + : new CultureInfo(uiCulture) + ); } public static IDisposable Use([NotNull] CultureInfo culture, CultureInfo uiCulture = null) @@ -29,5 +34,12 @@ namespace Volo.Abp.Localization CultureInfo.CurrentUICulture = currentUiCulture; }); } + + public static string GetBaseCultureName(string cultureName) + { + return cultureName.Contains("-") + ? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal)) + : cultureName; + } } } diff --git a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj index be953a1f26..a2f83399a0 100644 --- a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj +++ b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj @@ -14,24 +14,21 @@ - - - - - - + + + diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs index c2e29a7ff7..afcbaf6aef 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.BackgroundJobs; +using Volo.Abp.BackgroundJobs; using Volo.Abp.Emailing.Localization; -using Volo.Abp.Emailing.Templates; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Settings; +using Volo.Abp.TextTemplating; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Emailing @@ -15,15 +12,11 @@ namespace Volo.Abp.Emailing typeof(AbpSettingsModule), typeof(AbpVirtualFileSystemModule), typeof(AbpBackgroundJobsAbstractionsModule), - typeof(AbpLocalizationModule) + typeof(AbpLocalizationModule), + typeof(AbpTextTemplatingModule) )] public class AbpEmailingModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - AutoAddDefinitionProviders(context.Services); - } - public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => @@ -43,41 +36,5 @@ namespace Volo.Abp.Emailing options.AddJob(); }); } - - private static void AutoAddDefinitionProviders(IServiceCollection services) - { - var definitionProviders = new List(); - - services.OnRegistred(context => - { - - if (typeof(IEmailTemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) - { - definitionProviders.Add(context.ImplementationType); - } - }); - - services.Configure(options => - { - options.DefinitionProviders.AddIfNotContains(definitionProviders); - }); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - var emailTemplateDefinitionManager = - scope.ServiceProvider.GetRequiredService(); - - foreach (var templateDefinition in emailTemplateDefinitionManager.GetAll()) - { - foreach (var contributor in templateDefinition.Contributors) - { - contributor.Initialize(new EmailTemplateInitializationContext(templateDefinition, scope.ServiceProvider)); - } - } - } - } } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs deleted file mode 100644 index cb5a9d370b..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Volo.Abp.Collections; - -namespace Volo.Abp.Emailing.Templates -{ - public class AbpEmailTemplateOptions - { - public string DefaultLayout { get; set; } - - public ITypeList DefinitionProviders { get; } - - public AbpEmailTemplateOptions() - { - DefaultLayout = StandardEmailTemplates.DefaultLayout; - - DefinitionProviders = new TypeList(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs index c15012f0bf..385e0ef178 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs @@ -1,16 +1,26 @@ -using Volo.Abp.Emailing.Templates.VirtualFiles; +using Volo.Abp.TextTemplating; namespace Volo.Abp.Emailing.Templates { - public class DefaultEmailTemplateProvider : EmailTemplateDefinitionProvider + public class DefaultEmailTemplateProvider : TemplateDefinitionProvider { - public override void Define(IEmailTemplateDefinitionContext context) + public override void Define(ITemplateDefinitionContext context) { - context.Add(new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, defaultCultureName: "en", isLayout: true, layout: null) - .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout")); + context.Add( + new TemplateDefinition( + StandardEmailTemplates.Layout, + defaultCultureName: "en", + isLayout: true + ).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Layout") + ); - context.Add(new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage, defaultCultureName: "en") - .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message")); + context.Add( + new TemplateDefinition( + StandardEmailTemplates.Message, + defaultCultureName: "en", + layout: StandardEmailTemplates.Layout + ).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Message") + ); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl deleted file mode 100644 index 6454b0b93a..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl +++ /dev/null @@ -1 +0,0 @@ -{{message}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs deleted file mode 100644 index ad6f8c4839..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Text; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplate - { - public EmailTemplateDefinition Definition { get; } - - public string Content => ContentBuilder.ToString(); - - protected StringBuilder ContentBuilder { get; set; } - - public EmailTemplate(string content, EmailTemplateDefinition definition) - { - ContentBuilder = new StringBuilder(content); - Definition = definition; - } - - public virtual void SetLayout(EmailTemplate layoutTemplate) - { - if (!layoutTemplate.Definition.IsLayout) - { - throw new AbpException($"Given template is not a layout template: {layoutTemplate.Definition.Name}"); - } - - var newStrBuilder = new StringBuilder(layoutTemplate.Content); - newStrBuilder.Replace("{{#content}}", ContentBuilder.ToString()); - - ContentBuilder = newStrBuilder; - } - - public virtual void SetContent(string content) - { - ContentBuilder = new StringBuilder(content); - } - - public virtual void Replace(string name, string value) - { - ContentBuilder.Replace("{{" + name + "}}", value); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs deleted file mode 100644 index 44a91212ea..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateContributorList : List - { - public string GetOrNull(string cultureName) - { - foreach (var contributor in this.AsQueryable().Reverse()) - { - var templateString = contributor.GetOrNull(cultureName); - if (templateString != null) - { - return templateString; - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs deleted file mode 100644 index 19f0eb2fa7..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using JetBrains.Annotations; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinition - { - public const string DefaultLayoutPlaceHolder = "_"; - - public string Name { get; } - - public bool IsLayout { get; } - - public string Layout { get; set; } - - public Type LocalizationResource { get; set; } - - public EmailTemplateContributorList Contributors { get; } - - public string DefaultCultureName { get; } - - public bool SingleTemplateFile { get; } - - public EmailTemplateDefinition([NotNull] string name, Type localizationResource = null, bool isLayout = false, - string layout = DefaultLayoutPlaceHolder, string defaultCultureName = null, bool singleTemplateFile = false) - { - Name = Check.NotNullOrWhiteSpace(name, nameof(name)); - LocalizationResource = localizationResource; - Contributors = new EmailTemplateContributorList(); - IsLayout = isLayout; - Layout = layout; - DefaultCultureName = defaultCultureName; - SingleTemplateFile = singleTemplateFile; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs deleted file mode 100644 index 03a6c95d8b..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinitionContext : IEmailTemplateDefinitionContext - { - protected Dictionary EmailTemplates { get; } - - public EmailTemplateDefinitionContext(Dictionary emailTemplates) - { - EmailTemplates = emailTemplates; - } - - public virtual EmailTemplateDefinition GetOrNull(string name) - { - return EmailTemplates.GetOrDefault(name); - } - - public virtual IReadOnlyList GetAll() - { - return EmailTemplates.Values.ToImmutableList(); - } - - public virtual void Add(params EmailTemplateDefinition[] definitions) - { - if (definitions.IsNullOrEmpty()) - { - return; - } - - foreach (var definition in definitions) - { - EmailTemplates[definition.Name] = definition; - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs deleted file mode 100644 index aa36232156..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinitionDictionary : Dictionary - { - public EmailTemplateDefinitionDictionary Add(EmailTemplateDefinition emailTemplateDefinition) - { - if (ContainsKey(emailTemplateDefinition.Name)) - { - throw new AbpException( - "There is already an email template definition with given name: " + - emailTemplateDefinition.Name - ); - } - - this[emailTemplateDefinition.Name] = emailTemplateDefinition; - - return this; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs deleted file mode 100644 index 0491dc867e..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinitionManager : IEmailTemplateDefinitionManager, ISingletonDependency - { - protected Lazy> EmailTemplateDefinitions { get; } - - protected AbpEmailTemplateOptions Options { get; } - - protected IServiceProvider ServiceProvider { get; } - - public EmailTemplateDefinitionManager( - IOptions options, - IServiceProvider serviceProvider) - { - ServiceProvider = serviceProvider; - Options = options.Value; - - EmailTemplateDefinitions = - new Lazy>(CreateEmailTemplateDefinitions, true); - } - - public virtual EmailTemplateDefinition Get(string name) - { - Check.NotNull(name, nameof(name)); - - var template = GetOrNull(name); - - if (template == null) - { - throw new AbpException("Undefined template: " + name); - } - - return template; - } - - public virtual IReadOnlyList GetAll() - { - return EmailTemplateDefinitions.Value.Values.ToImmutableList(); - } - - public virtual EmailTemplateDefinition GetOrNull(string name) - { - return EmailTemplateDefinitions.Value.GetOrDefault(name); - } - - protected virtual IDictionary CreateEmailTemplateDefinitions() - { - var templates = new Dictionary(); - - using (var scope = ServiceProvider.CreateScope()) - { - var providers = Options - .DefinitionProviders - .Select(p => scope.ServiceProvider.GetRequiredService(p) as IEmailTemplateDefinitionProvider) - .ToList(); - - foreach (var provider in providers) - { - provider.Define(new EmailTemplateDefinitionContext(templates)); - } - } - - return templates; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs deleted file mode 100644 index e53505fafc..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Emailing.Templates -{ - public abstract class EmailTemplateDefinitionProvider : IEmailTemplateDefinitionProvider, ITransientDependency - { - public abstract void Define(IEmailTemplateDefinitionContext context); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs deleted file mode 100644 index 8cd4b95bbd..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateInitializationContext - { - public EmailTemplateDefinition EmailTemplateDefinition { get; } - - public IServiceProvider ServiceProvider { get; } - - public EmailTemplateInitializationContext(EmailTemplateDefinition emailTemplateDefinition, - IServiceProvider serviceProvider) - { - EmailTemplateDefinition = emailTemplateDefinition; - ServiceProvider = serviceProvider; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs deleted file mode 100644 index 29fcf08664..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; -using Microsoft.Extensions.Localization; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Localization; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateProvider : IEmailTemplateProvider, ITransientDependency - { - protected IEmailTemplateDefinitionManager EmailTemplateDefinitionManager; - protected ITemplateLocalizer TemplateLocalizer { get; } - protected AbpEmailTemplateOptions Options { get; } - protected IStringLocalizerFactory StringLocalizerFactory; - - public EmailTemplateProvider(IEmailTemplateDefinitionManager emailTemplateDefinitionManager, - ITemplateLocalizer templateLocalizer, IStringLocalizerFactory stringLocalizerFactory, - IOptions options) - { - EmailTemplateDefinitionManager = emailTemplateDefinitionManager; - TemplateLocalizer = templateLocalizer; - StringLocalizerFactory = stringLocalizerFactory; - Options = options.Value; - } - - public async Task GetAsync(string name) - { - return await GetAsync(name, CultureInfo.CurrentUICulture.Name); - } - - public async Task GetAsync(string name, string cultureName) - { - return await GetInternalAsync(name, cultureName); - } - - protected virtual async Task GetInternalAsync(string name, string cultureName) - { - var emailTemplateDefinition = EmailTemplateDefinitionManager.GetOrNull(name); - if (emailTemplateDefinition == null) - { - // TODO: Localized message - throw new AbpException($"email template {name} not definition"); - } - - var emailTemplateString = emailTemplateDefinition.Contributors.GetOrNull(cultureName); - if (emailTemplateString == null && emailTemplateDefinition.DefaultCultureName != null) - { - emailTemplateString = - emailTemplateDefinition.Contributors.GetOrNull(emailTemplateDefinition.DefaultCultureName); - if (emailTemplateString != null) - { - cultureName = emailTemplateDefinition.DefaultCultureName; - } - } - - if (emailTemplateString != null) - { - var emailTemplate = new EmailTemplate(emailTemplateString, emailTemplateDefinition); - - await SetLayoutAsync(emailTemplateDefinition, emailTemplate, cultureName); - - if (emailTemplateDefinition.SingleTemplateFile) - { - await LocalizeAsync(emailTemplateDefinition, emailTemplate, cultureName); - } - - return emailTemplate; - } - - // TODO: Localized message - throw new AbpException($"{cultureName} template not exist!"); - } - - protected virtual async Task SetLayoutAsync(EmailTemplateDefinition emailTemplateDefinition, - EmailTemplate emailTemplate, string cultureName) - { - var layout = emailTemplateDefinition.Layout; - if (layout.IsNullOrWhiteSpace()) - { - return; - } - - if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder) - { - layout = Options.DefaultLayout; - } - - var layoutTemplate = await GetInternalAsync(layout, cultureName); - - emailTemplate.SetLayout(layoutTemplate); - } - - protected virtual Task LocalizeAsync(EmailTemplateDefinition emailTemplateDefinition, - EmailTemplate emailTemplate, string cultureName) - { - if (emailTemplateDefinition.LocalizationResource == null) - { - return Task.CompletedTask; - } - - var localizer = StringLocalizerFactory.Create(emailTemplateDefinition.LocalizationResource); - if (cultureName != null) - { - using (CultureHelper.Use(new CultureInfo(cultureName))) - { - emailTemplate.SetContent(TemplateLocalizer.Localize(localizer, emailTemplate.Content)); - } - } - else - { - emailTemplate.SetContent( - TemplateLocalizer.Localize(localizer, emailTemplate.Content) - ); - } - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs deleted file mode 100644 index d2c2775845..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateContributor - { - void Initialize(EmailTemplateInitializationContext context); - - string GetOrNull(string cultureName); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs deleted file mode 100644 index 1641562ccf..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateDefinitionContext - { - EmailTemplateDefinition GetOrNull(string name); - - void Add(params EmailTemplateDefinition[] definitions); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs deleted file mode 100644 index 0936a2fe93..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using JetBrains.Annotations; - -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateDefinitionManager - { - [NotNull] - EmailTemplateDefinition Get([NotNull] string name); - - IReadOnlyList GetAll(); - - EmailTemplateDefinition GetOrNull(string name); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs deleted file mode 100644 index 691d3874d6..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateDefinitionProvider - { - void Define(IEmailTemplateDefinitionContext context); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs deleted file mode 100644 index ab68dbe2ca..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateProvider - { - Task GetAsync(string name); - - Task GetAsync(string name, string cultureName); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs deleted file mode 100644 index 35ac14c8fd..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface ITemplateRender - { - Task RenderAsync(string template, object model = null); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl similarity index 84% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl index 107fbb5230..57453a027f 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl @@ -4,6 +4,6 @@ - {{#content}} + {{content}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl new file mode 100644 index 0000000000..349de66b36 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl @@ -0,0 +1 @@ +{{model.message}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs index 7e8cbedc68..6a60219a5e 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs @@ -2,7 +2,7 @@ { public static class StandardEmailTemplates { - public const string DefaultLayout = "Abp.DefaultLayout"; - public const string SimpleMessage = "Abp.SimpleMessage"; + public const string Layout = "Abp.StandardEmailTemplates.Layout"; + public const string Message = "Abp.StandardEmailTemplates.Message"; } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs deleted file mode 100644 index 8c4e24017c..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; -using Scriban; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Emailing.Templates -{ - public class TemplateRender : ITemplateRender, ITransientDependency - { - public async Task RenderAsync(string template, object model = null) - { - var scribanTemplate = Template.Parse(template); - return await scribanTemplate.RenderAsync(model); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs deleted file mode 100644 index 7bee611d28..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Volo.Abp.Emailing.Templates.VirtualFiles -{ - public static class EmailTemplateDefinitionExtensions - { - public static EmailTemplateDefinition AddTemplateVirtualFile( - this EmailTemplateDefinition emailTemplateDefinition, string path) - { - emailTemplateDefinition.Contributors.Add(new SingleVirtualFileEmailTemplateContributor(path)); - return emailTemplateDefinition; - } - - public static EmailTemplateDefinition AddTemplateVirtualFiles( - this EmailTemplateDefinition emailTemplateDefinition, string path) - { - emailTemplateDefinition.Contributors.Add(new MultipleVirtualFilesEmailTemplateContributor(path)); - return emailTemplateDefinition; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs deleted file mode 100644 index 2ae5f88cc5..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Volo.Abp.VirtualFileSystem; - -namespace Volo.Abp.Emailing.Templates.VirtualFiles -{ - public class MultipleVirtualFilesEmailTemplateContributor : IEmailTemplateContributor - { - private readonly string _virtualPath; - - private IVirtualFileProvider _virtualFileProvider; - - private Dictionary _templateDictionary; - - private readonly object _syncObj = new object(); - - public MultipleVirtualFilesEmailTemplateContributor(string virtualPath) - { - _virtualPath = virtualPath; - } - - public void Initialize(EmailTemplateInitializationContext context) - { - _virtualFileProvider = context.ServiceProvider.GetRequiredService(); - } - - public string GetOrNull(string cultureName) - { - return GetTemplateDictionary().GetOrDefault(cultureName); - } - - private Dictionary GetTemplateDictionary() - { - var dictionaries = _templateDictionary; - if (dictionaries != null) - { - return dictionaries; - } - - lock (_syncObj) - { - dictionaries = _templateDictionary; - if (dictionaries != null) - { - return dictionaries; - } - - _templateDictionary = new Dictionary(); - foreach (var file in _virtualFileProvider.GetDirectoryContents(_virtualPath)) - { - if (file.IsDirectory) - { - continue; - } - - // TODO: How to normalize file names? - _templateDictionary.Add(file.Name.RemovePostFix(".tpl"), file.ReadAsString()); - } - - dictionaries = _templateDictionary; - } - - return dictionaries; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs deleted file mode 100644 index d72d18e99a..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Volo.Abp.VirtualFileSystem; - -namespace Volo.Abp.Emailing.Templates.VirtualFiles -{ - public class SingleVirtualFileEmailTemplateContributor : IEmailTemplateContributor - { - private readonly string _virtualPath; - - private IVirtualFileProvider _virtualFileProvider; - - public SingleVirtualFileEmailTemplateContributor(string virtualPath) - { - _virtualPath = virtualPath; - } - - public void Initialize(EmailTemplateInitializationContext context) - { - _virtualFileProvider = context.ServiceProvider.GetRequiredService(); - } - - public string GetOrNull(string cultureName) - { - var file = _virtualFileProvider.GetFileInfo(_virtualPath); - if (file == null || !file.Exists || file.IsDirectory) - { - return null; - } - - return file.ReadAsString(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs index af728686fd..8db091eb30 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs @@ -32,7 +32,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory IAuditingStore auditingStore, IOptions options, IClock clock, - IJsonSerializer jsonSerializer, + IJsonSerializer jsonSerializer, IAuditingHelper auditingHelper) { _clock = clock; @@ -241,7 +241,8 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory } } - if (propertyEntry.IsModified) + var isModified = !(propertyEntry.OriginalValue?.Equals(propertyEntry.CurrentValue) ?? propertyEntry.CurrentValue == null); + if (isModified) { return true; } diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs index 79c164eb5f..53a2d7714e 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs @@ -104,7 +104,7 @@ namespace Volo.Abp.Localization //Try to get from same language dictionary (without country code) if (cultureName.Contains("-")) //Example: "tr-TR" (length=5) { - var strLang = Resource.Contributors.GetOrNull(GetBaseCultureName(cultureName), name); + var strLang = Resource.Contributors.GetOrNull(CultureHelper.GetBaseCultureName(cultureName), name); if (strLang != null) { return strLang; @@ -168,7 +168,7 @@ namespace Volo.Abp.Localization //Overwrite all strings from the language based on country culture if (cultureName.Contains("-")) { - Resource.Contributors.Fill(GetBaseCultureName(cultureName), allStrings); + Resource.Contributors.Fill(CultureHelper.GetBaseCultureName(cultureName), allStrings); } } @@ -178,12 +178,7 @@ namespace Volo.Abp.Localization return allStrings.Values.ToImmutableList(); } - protected virtual string GetBaseCultureName(string cultureName) - { - return cultureName.Contains("-") - ? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal)) - : cultureName; - } + public class CultureWrapperStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance { diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs index daa6abe667..a81adf3562 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.Localization { public LocalizedString GetOrNull(string cultureName, string name) { - foreach (var contributor in this.AsQueryable().Reverse()) //TODO: Reverse? + foreach (var contributor in this.AsQueryable().Reverse()) { var localString = contributor.GetOrNull(cultureName, name); if (localString != null) diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs deleted file mode 100644 index 5c4220c45f..0000000000 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.RegularExpressions; -using Microsoft.Extensions.Localization; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Localization -{ - public class TemplateLocalizer : ITemplateLocalizer, ITransientDependency - { - public string Localize(IStringLocalizer localizer, string text) - { - return new Regex("\\{\\{#L:.+?\\}\\}") - .Replace( - text, - match => localizer[match.Value.Substring(5, match.Length - 7)] - ); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj new file mode 100644 index 0000000000..7bcc0400b5 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj @@ -0,0 +1,26 @@ + + + + + + + netstandard2.0 + Volo.Abp.TextTemplating + Volo.Abp.TextTemplating + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs new file mode 100644 index 0000000000..8d76391829 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TextTemplating +{ + [DependsOn( + typeof(AbpVirtualFileSystemModule), + typeof(AbpLocalizationAbstractionsModule) + )] + public class AbpTextTemplatingModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AutoAddProvidersAndContributors(context.Services); + } + + private static void AutoAddProvidersAndContributors(IServiceCollection services) + { + var definitionProviders = new List(); + var contentContributors = new List(); + + services.OnRegistred(context => + { + if (typeof(ITemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) + { + definitionProviders.Add(context.ImplementationType); + } + + if (typeof(ITemplateContentContributor).IsAssignableFrom(context.ImplementationType)) + { + contentContributors.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + options.ContentContributors.AddIfNotContains(contentContributors); + }); + } + } +} diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs new file mode 100644 index 0000000000..b217094974 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Collections; + +namespace Volo.Abp.TextTemplating +{ + public class AbpTextTemplatingOptions + { + public ITypeList DefinitionProviders { get; } + public ITypeList ContentContributors { get; } + + public AbpTextTemplatingOptions() + { + DefinitionProviders = new TypeList(); + ContentContributors = new TypeList(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs new file mode 100644 index 0000000000..746b29a2f0 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateContentContributor + { + Task GetOrNullAsync(TemplateContentContributorContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs new file mode 100644 index 0000000000..19248dc161 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateContentProvider + { + Task GetContentOrNullAsync( + [NotNull] string templateName, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true + ); + + Task GetContentOrNullAsync( + [NotNull] TemplateDefinition templateDefinition, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs new file mode 100644 index 0000000000..515cee74ca --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateDefinitionContext + { + IReadOnlyList GetAll(string name); + + TemplateDefinition GetOrNull(string name); + + void Add(params TemplateDefinition[] definitions); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs new file mode 100644 index 0000000000..cbd2d15463 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateDefinitionManager + { + [NotNull] + TemplateDefinition Get([NotNull] string name); + + [NotNull] + IReadOnlyList GetAll(); + + [CanBeNull] + TemplateDefinition GetOrNull(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs new file mode 100644 index 0000000000..aeb6a3c2da --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateDefinitionProvider + { + void PreDefine(ITemplateDefinitionContext context); + + void Define(ITemplateDefinitionContext context); + + void PostDefine(ITemplateDefinitionContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs new file mode 100644 index 0000000000..b153a5130b --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.TextTemplating +{ + public interface ITemplateRenderer + { + /// + /// Renders a text template. + /// + /// The template name + /// An optional model object that is used in the template + /// Culture name. Uses the if not specified + /// A dictionary which can be used to import global objects to the template + /// + Task RenderAsync( + [NotNull] string templateName, + [CanBeNull] object model = null, + [CanBeNull] string cultureName = null, + [CanBeNull] Dictionary globalContext = null + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs new file mode 100644 index 0000000000..773bf1a0a4 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs @@ -0,0 +1,27 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateContentContributorContext + { + [NotNull] + public TemplateDefinition TemplateDefinition { get; } + + [NotNull] + public IServiceProvider ServiceProvider { get; } + + [CanBeNull] + public string Culture { get; } + + public TemplateContentContributorContext( + [NotNull] TemplateDefinition templateDefinition, + [NotNull] IServiceProvider serviceProvider, + [CanBeNull] string culture) + { + TemplateDefinition = Check.NotNull(templateDefinition, nameof(templateDefinition)); + ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); + Culture = culture; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs new file mode 100644 index 0000000000..55a3979e21 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs @@ -0,0 +1,170 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateContentProvider : ITemplateContentProvider, ITransientDependency + { + public IHybridServiceScopeFactory ServiceScopeFactory { get; } + public AbpTextTemplatingOptions Options { get; } + private readonly ITemplateDefinitionManager _templateDefinitionManager; + + public TemplateContentProvider( + ITemplateDefinitionManager templateDefinitionManager, + IHybridServiceScopeFactory serviceScopeFactory, + IOptions options) + { + ServiceScopeFactory = serviceScopeFactory; + Options = options.Value; + _templateDefinitionManager = templateDefinitionManager; + } + + public virtual Task GetContentOrNullAsync( + [NotNull] string templateName, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true) + { + var template = _templateDefinitionManager.Get(templateName); + return GetContentOrNullAsync(template, cultureName); + } + + public virtual async Task GetContentOrNullAsync( + [NotNull] TemplateDefinition templateDefinition, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true) + { + Check.NotNull(templateDefinition, nameof(templateDefinition)); + + if (!Options.ContentContributors.Any()) + { + throw new AbpException( + $"No template content contributor was registered. Use {nameof(AbpTextTemplatingOptions)} to register contributors!" + ); + } + + using (var scope = ServiceScopeFactory.CreateScope()) + { + string templateString = null; + + if (cultureName == null && useCurrentCultureIfCultureNameIsNull) + { + cultureName = CultureInfo.CurrentUICulture.Name; + } + + var contributors = CreateTemplateContentContributors(scope.ServiceProvider); + + //Try to get from the requested culture + templateString = await GetContentOrNullAsync( + contributors, + new TemplateContentContributorContext( + templateDefinition, + scope.ServiceProvider, + cultureName + ) + ); + + if (templateString != null) + { + return templateString; + } + + if (!tryDefaults) + { + return null; + } + + //Try to get from same culture without country code + if (cultureName != null && cultureName.Contains("-")) //Example: "tr-TR" + { + templateString = await GetContentOrNullAsync( + contributors, + new TemplateContentContributorContext( + templateDefinition, + scope.ServiceProvider, + CultureHelper.GetBaseCultureName(cultureName) + ) + ); + + if (templateString != null) + { + return templateString; + } + } + + if (templateDefinition.IsInlineLocalized) + { + //Try to get culture independent content + templateString = await GetContentOrNullAsync( + contributors, + new TemplateContentContributorContext( + templateDefinition, + scope.ServiceProvider, + null + ) + ); + + if (templateString != null) + { + return templateString; + } + } + else + { + //Try to get from default culture + if (templateDefinition.DefaultCultureName != null) + { + templateString = await GetContentOrNullAsync( + contributors, + new TemplateContentContributorContext( + templateDefinition, + scope.ServiceProvider, + templateDefinition.DefaultCultureName + ) + ); + + if (templateString != null) + { + return templateString; + } + } + } + } + + //Not found + return null; + } + + protected virtual ITemplateContentContributor[] CreateTemplateContentContributors(IServiceProvider serviceProvider) + { + return Options.ContentContributors + .Select(type => (ITemplateContentContributor)serviceProvider.GetRequiredService(type)) + .Reverse() + .ToArray(); + } + + protected virtual async Task GetContentOrNullAsync( + ITemplateContentContributor[] contributors, + TemplateContentContributorContext context) + { + foreach (var contributor in contributors) + { + var templateString = await contributor.GetOrNullAsync(context); + if (templateString != null) + { + return templateString; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs new file mode 100644 index 0000000000..db2ccf6f63 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Localization; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateDefinition + { + public const int MaxNameLength = 128; + + [NotNull] + public string Name { get; } + + [NotNull] + public ILocalizableString DisplayName + { + get => _displayName; + set + { + Check.NotNull(value, nameof(value)); + _displayName = value; + } + } + private ILocalizableString _displayName; + + public bool IsLayout { get; } + + [CanBeNull] + public string Layout { get; set; } + + [CanBeNull] + public Type LocalizationResource { get; set; } + + public bool IsInlineLocalized { get; set; } + + [CanBeNull] + public string DefaultCultureName { get; } + + /// + /// Gets/sets a key-value on the . + /// + /// Name of the property + /// + /// Returns the value in the dictionary by given . + /// Returns null if given is not present in the dictionary. + /// + [CanBeNull] + public object this[string name] + { + get => Properties.GetOrDefault(name); + set => Properties[name] = value; + } + + /// + /// Can be used to get/set custom properties for this feature. + /// + [NotNull] + public Dictionary Properties { get; } + + public TemplateDefinition( + [NotNull] string name, + [CanBeNull] Type localizationResource = null, + [CanBeNull] ILocalizableString displayName = null, + bool isLayout = false, + string layout = null, + string defaultCultureName = null) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), MaxNameLength); + LocalizationResource = localizationResource; + DisplayName = displayName ?? new FixedLocalizableString(Name); + IsLayout = isLayout; + Layout = layout; + DefaultCultureName = defaultCultureName; + Properties = new Dictionary(); + } + + /// + /// Sets a property in the dictionary. + /// This is a shortcut for nested calls on this object. + /// + public virtual TemplateDefinition WithProperty(string key, object value) + { + Properties[key] = value; + return this; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs new file mode 100644 index 0000000000..04c3876531 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateDefinitionContext : ITemplateDefinitionContext + { + protected Dictionary Templates { get; } + + public TemplateDefinitionContext(Dictionary templates) + { + Templates = templates; + } + + public IReadOnlyList GetAll(string name) + { + return Templates.Values.ToImmutableList(); + } + + public virtual TemplateDefinition GetOrNull(string name) + { + return Templates.GetOrDefault(name); + } + + public virtual IReadOnlyList GetAll() + { + return Templates.Values.ToImmutableList(); + } + + public virtual void Add(params TemplateDefinition[] definitions) + { + if (definitions.IsNullOrEmpty()) + { + return; + } + + foreach (var definition in definitions) + { + Templates[definition.Name] = definition; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs new file mode 100644 index 0000000000..0d17e9969c --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.TextTemplating.VirtualFiles; + +namespace Volo.Abp.TextTemplating +{ + public static class TemplateDefinitionExtensions + { + public static TemplateDefinition WithVirtualFilePath( + [NotNull] this TemplateDefinition templateDefinition, + [NotNull] string virtualPath) + { + Check.NotNull(templateDefinition, nameof(templateDefinition)); + + return templateDefinition.WithProperty( + VirtualFileTemplateContentContributor.VirtualPathPropertyName, + virtualPath + ); + } + + public static string GetVirtualFilePathOrNull( + [NotNull] this TemplateDefinition templateDefinition) + { + Check.NotNull(templateDefinition, nameof(templateDefinition)); + + return templateDefinition + .Properties + .GetOrDefault(VirtualFileTemplateContentContributor.VirtualPathPropertyName) as string; + } + } +} diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs new file mode 100644 index 0000000000..11081f6dff --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateDefinitionManager : ITemplateDefinitionManager, ISingletonDependency + { + protected Lazy> TemplateDefinitions { get; } + + protected AbpTextTemplatingOptions Options { get; } + + protected IServiceProvider ServiceProvider { get; } + + public TemplateDefinitionManager( + IOptions options, + IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + Options = options.Value; + + TemplateDefinitions = + new Lazy>(CreateTextTemplateDefinitions, true); + } + + public virtual TemplateDefinition Get(string name) + { + Check.NotNull(name, nameof(name)); + + var template = GetOrNull(name); + + if (template == null) + { + throw new AbpException("Undefined template: " + name); + } + + return template; + } + + public virtual IReadOnlyList GetAll() + { + return TemplateDefinitions.Value.Values.ToImmutableList(); + } + + public virtual TemplateDefinition GetOrNull(string name) + { + return TemplateDefinitions.Value.GetOrDefault(name); + } + + protected virtual IDictionary CreateTextTemplateDefinitions() + { + var templates = new Dictionary(); + + using (var scope = ServiceProvider.CreateScope()) + { + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as ITemplateDefinitionProvider) + .ToList(); + + var context = new TemplateDefinitionContext(templates); + + foreach (var provider in providers) + { + provider.PreDefine(context); + } + + foreach (var provider in providers) + { + provider.Define(context); + } + + foreach (var provider in providers) + { + provider.PostDefine(context); + } + + SetIsInlineLocalized( + templates, + scope.ServiceProvider + ); + } + + return templates; + } + + protected virtual void SetIsInlineLocalized( + Dictionary templates, + IServiceProvider serviceProvider) + { + var virtualFileProvider = serviceProvider.GetRequiredService(); + + foreach (var templateDefinition in templates.Values) + { + var virtualPath = templateDefinition.GetVirtualFilePathOrNull(); + if (virtualPath == null) + { + continue; + } + + var fileInfo = virtualFileProvider.GetFileInfo(virtualPath); + if (!fileInfo.Exists) + { + throw new AbpException("Could not find a file/folder at the location: " + virtualPath); + } + + templateDefinition.IsInlineLocalized = !fileInfo.IsDirectory; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs new file mode 100644 index 0000000000..924a6dfdd8 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs @@ -0,0 +1,19 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.TextTemplating +{ + public abstract class TemplateDefinitionProvider : ITemplateDefinitionProvider, ITransientDependency + { + public virtual void PreDefine(ITemplateDefinitionContext context) + { + + } + + public abstract void Define(ITemplateDefinitionContext context); + + public virtual void PostDefine(ITemplateDefinitionContext context) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs new file mode 100644 index 0000000000..f51e8447b0 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.Localization; +using Scriban; +using Scriban.Runtime; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateRenderer : ITemplateRenderer, ITransientDependency + { + private readonly ITemplateContentProvider _templateContentProvider; + private readonly ITemplateDefinitionManager _templateDefinitionManager; + private readonly IStringLocalizerFactory _stringLocalizerFactory; + + public TemplateRenderer( + ITemplateContentProvider templateContentProvider, + ITemplateDefinitionManager templateDefinitionManager, + IStringLocalizerFactory stringLocalizerFactory) + { + _templateContentProvider = templateContentProvider; + _templateDefinitionManager = templateDefinitionManager; + _stringLocalizerFactory = stringLocalizerFactory; + } + + public virtual async Task RenderAsync( + [NotNull] string templateName, + [CanBeNull] object model = null, + [CanBeNull] string cultureName = null, + [CanBeNull] Dictionary globalContext = null) + { + Check.NotNullOrWhiteSpace(templateName, nameof(templateName)); + + if (globalContext == null) + { + globalContext = new Dictionary(); + } + + if (cultureName == null) + { + return await RenderInternalAsync( + templateName, + globalContext, + model + ); + } + else + { + using (CultureHelper.Use(cultureName)) + { + return await RenderInternalAsync( + templateName, + globalContext, + model + ); + } + } + } + + protected virtual async Task RenderInternalAsync( + string templateName, + Dictionary globalContext, + object model = null) + { + var templateDefinition = _templateDefinitionManager.Get(templateName); + + var renderedContent = await RenderSingleTemplateAsync( + templateDefinition, + globalContext, + model + ); + + if (templateDefinition.Layout != null) + { + globalContext["content"] = renderedContent; + renderedContent = await RenderInternalAsync( + templateDefinition.Layout, + globalContext + ); + } + + return renderedContent; + } + + protected virtual async Task RenderSingleTemplateAsync( + TemplateDefinition templateDefinition, + Dictionary globalContext, + object model = null) + { + var rawTemplateContent = await _templateContentProvider + .GetContentOrNullAsync( + templateDefinition + ); + + return await RenderTemplateContentWithScribanAsync( + templateDefinition, + rawTemplateContent, + globalContext, + model + ); + } + + protected virtual async Task RenderTemplateContentWithScribanAsync( + TemplateDefinition templateDefinition, + string templateContent, + Dictionary globalContext, + object model = null) + { + var context = CreateScribanTemplateContext( + templateDefinition, + globalContext, + model + ); + + return await Template + .Parse(templateContent) + .RenderAsync(context); + } + + protected virtual TemplateContext CreateScribanTemplateContext( + TemplateDefinition templateDefinition, + Dictionary globalContext, + object model = null) + { + var context = new TemplateContext(); + + var scriptObject = new ScriptObject(); + + scriptObject.Import(globalContext); + + if (model != null) + { + scriptObject["model"] = model; + } + + if (templateDefinition.LocalizationResource != null) + { + var localizer = _stringLocalizerFactory.Create(templateDefinition.LocalizationResource); + scriptObject.Import( + "L", + new Func( + name => localizer[name] + ) + ); + } + + context.PushGlobal(scriptObject); + + return context; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..cde913f307 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.FileProviders; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public class FileInfoLocalizedTemplateContentReader : ILocalizedTemplateContentReader + { + private string _content; + + public async Task ReadContentsAsync(IFileInfo fileInfo) + { + _content = await fileInfo.ReadAsStringAsync(); + } + + public string GetContentOrNull(string culture) + { + if (culture == null) + { + return _content; + } + + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..7d837d731d --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs @@ -0,0 +1,9 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public interface ILocalizedTemplateContentReader + { + public string GetContentOrNull([CanBeNull] string culture); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs new file mode 100644 index 0000000000..2d4d9ef2d5 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public interface ILocalizedTemplateContentReaderFactory + { + Task CreateAsync(TemplateDefinition templateDefinition); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs new file mode 100644 index 0000000000..b30e5771f4 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency + { + private readonly IVirtualFileProvider _virtualFileProvider; + private readonly Dictionary _readerCache; + private readonly ReaderWriterLockSlim _lock; + + public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) + { + _virtualFileProvider = virtualFileProvider; + _readerCache = new Dictionary(); + _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + } + + public async Task CreateAsync(TemplateDefinition templateDefinition) + { + _lock.EnterUpgradeableReadLock(); + + try + { + var reader = _readerCache.GetOrDefault(templateDefinition.Name); + if (reader != null) + { + return reader; + } + + _lock.EnterWriteLock(); + + try + { + reader = await CreateInternalAsync(templateDefinition); + _readerCache[templateDefinition.Name] = reader; + return reader; + } + finally + { + _lock.ExitWriteLock(); + } + } + finally + { + _lock.ExitUpgradeableReadLock(); + } + } + + protected virtual async Task CreateInternalAsync( + TemplateDefinition templateDefinition) + { + var virtualPath = templateDefinition.GetVirtualFilePathOrNull(); + if (virtualPath == null) + { + return NullLocalizedTemplateContentReader.Instance; + } + + var fileInfo = _virtualFileProvider.GetFileInfo(virtualPath); + if (!fileInfo.Exists) + { + throw new AbpException("Could not find a file/folder at the location: " + virtualPath); + } + + if (fileInfo.IsDirectory) + { + var folderReader = new VirtualFolderLocalizedTemplateContentReader(); + await folderReader.ReadContentsAsync(_virtualFileProvider, virtualPath); + return folderReader; + } + else //File + { + var singleFileReader = new FileInfoLocalizedTemplateContentReader(); + await singleFileReader.ReadContentsAsync(fileInfo); + return singleFileReader; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..475559c634 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs @@ -0,0 +1,17 @@ +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public class NullLocalizedTemplateContentReader : ILocalizedTemplateContentReader + { + public static NullLocalizedTemplateContentReader Instance { get; } = new NullLocalizedTemplateContentReader(); + + private NullLocalizedTemplateContentReader() + { + + } + + public string GetContentOrNull(string culture) + { + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs new file mode 100644 index 0000000000..ede33a3652 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public class VirtualFileTemplateContentContributor : ITemplateContentContributor, ITransientDependency + { + public const string VirtualPathPropertyName = "VirtualPath"; + + private readonly ILocalizedTemplateContentReaderFactory _localizedTemplateContentReaderFactory; + + public VirtualFileTemplateContentContributor( + ILocalizedTemplateContentReaderFactory localizedTemplateContentReaderFactory) + { + _localizedTemplateContentReaderFactory = localizedTemplateContentReaderFactory; + } + + public virtual async Task GetOrNullAsync(TemplateContentContributorContext context) + { + var localizedReader = await _localizedTemplateContentReaderFactory + .CreateAsync(context.TemplateDefinition); + + return localizedReader.GetContentOrNull( + context.Culture + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..638a7db190 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.FileProviders; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + public class VirtualFolderLocalizedTemplateContentReader : ILocalizedTemplateContentReader + { + private Dictionary _dictionary; + + public async Task ReadContentsAsync( + IVirtualFileProvider virtualFileProvider, + string virtualPath) + { + _dictionary = new Dictionary(); + + var directoryInfo = virtualFileProvider.GetFileInfo(virtualPath); + if (!directoryInfo.IsDirectory) + { + throw new AbpException("Given virtual path is not a folder: " + virtualPath); + } + + foreach (var file in virtualFileProvider.GetDirectoryContents(virtualPath)) + { + if (file.IsDirectory) + { + continue; + } + + _dictionary.Add(file.Name.RemovePostFix(".tpl"), await file.ReadAsStringAsync()); + } + } + + public string GetContentOrNull(string cultureName) + { + if (cultureName == null) + { + return null; + } + + return _dictionary.GetOrDefault(cultureName); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl-PL.json similarity index 72% rename from framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl.json rename to framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl-PL.json index 557d01eb31..d8edcb1438 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl.json +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:Administration": "Administracja" } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json similarity index 98% rename from framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl.json rename to framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json index cdaf556168..1be12a51cf 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "InternalServerErrorMessage": "Błąd wewnętrzny serwera podczas przetwarzania żądania!", "ValidationErrorMessage": "Twoje żądanie jest niepoprawnie!", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json index 2be5ff3b15..d202abe1eb 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "V poli {0} musí být řežezec nebo řada o minimální délce '{1}'.", "The {0} field is not a valid phone number.": "V poli {0} není platné telefonní číslo.", "The field {0} must be between {1} and {2}.": "Pole {0} musí být mezi {1} a {2}.", - "The field {0} must match the regular expression '{1}'.": "Pole {0} musí odpovídat regulérnímu výrazu '{1}'.", + "The field {0} must match the regular expression '{1}'.": "Pole {0} neodpovídá požadovanému formátu.", "The {0} field is required.": "Pole {0} je povinné.", "The field {0} must be a string with a maximum length of {1}.": "Pole {0} musí být řetězec o maximální délce {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí být řetězec o minimální délce {2} a maximální délce {1} znaků.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json index b6ff1bb26c..74c6670e55 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "The field {0} must be a string or array type with a minimum length of '{1}'.", "The {0} field is not a valid phone number.": "The {0} field is not a valid phone number.", "The field {0} must be between {1} and {2}.": "The field {0} must be between {1} and {2}.", - "The field {0} must match the regular expression '{1}'.": "The field {0} must match the regular expression '{1}'.", + "The field {0} must match the regular expression '{1}'.": "The field {0} does not match the requested format.", "The {0} field is required.": "The {0} field is required.", "The field {0} must be a string with a maximum length of {1}.": "The field {0} must be a string with a maximum length of {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json index 25ed44d7ad..1615d7db42 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "El campo {0} debe ser una cadena o un array con una longitud mínima de '{1}'.", "The {0} field is not a valid phone number.": "{0} no es un número de teléfono válido.", "The field {0} must be between {1} and {2}.": "El campo {0} debe ser entre {1} y {2}.", - "The field {0} must match the regular expression '{1}'.": "El campo {0} debe coincidir con la expresión regular '{1}'.", + "The field {0} must match the regular expression '{1}'.": "El campo {0} no coincide con el formato solicitado.", "The {0} field is required.": "La {0} campo es obligatorio.", "The field {0} must be a string with a maximum length of {1}.": "El campo {0} debe ser una cadena con una longitud máxima de {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "El campo {0} debe ser una cadena con una longitud mínima de {2} y una longitud máxima de {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json similarity index 95% rename from framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl.json rename to framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json index eef572eb13..80dc72212d 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "'{0}' and '{1}' do not match.": "'{0}' i '{1}' nie są takie same.", "The {0} field is not a valid credit card number.": "Pole {0} nie jest poprawnym numerem karty kredytowej.", @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "Pole {0} musi być łańcuchem znaków lub talicą o minimalnej długości '{1}'.", "The {0} field is not a valid phone number.": "Pole {0} nie jest poprawnym numerem telefonu.", "The field {0} must be between {1} and {2}.": "Pole {0} musi być pomiędzy {1} i {2}.", - "The field {0} must match the regular expression '{1}'.": "Pole {0} musi pasować do wyrażenia regularnego '{1}'.", + "The field {0} must match the regular expression '{1}'.": "Pole {0} nie pasuje do żądanego formatu.", "The {0} field is required.": "Pole {0} jest wymagane.", "The field {0} must be a string with a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o maksymalnej długości {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o minimalnej długości {2} i maksymalnej długości {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json index b0852a3f5f..8d799a7730 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "O campo {0} deve ser uma palavra ou matriz com o tamanho mínimo de '{1}'.", "The {0} field is not a valid phone number.": "O campo {0} não é um telefone válido.", "The field {0} must be between {1} and {2}.": "O campo {0} deve estar entre {1} e {2}.", - "The field {0} must match the regular expression '{1}'.": "O campo {0} deve ser compatível com a expressão regular '{1}'.", + "The field {0} must match the regular expression '{1}'.": "O campo {0} não corresponde ao formato solicitado.", "The {0} field is required.": "O Campo {0} é obrigatório.", "The field {0} must be a string with a maximum length of {1}.": "O campo {0} deve ser uma palavra com o tamanho máximo de {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "O campo {0} deve ser uma palavra com o tamanho mínimo de {2} e tamanho máximo de {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json index 6272fdf534..d718dbbed8 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "Поле {0} должно иметь тип строки или массива с минимальной длиной '{1}'.", "The {0} field is not a valid phone number.": "Поле {0} не содержит действительный номер телефона.", "The field {0} must be between {1} and {2}.": "Поле {0} должно находиться между {1} и {2}.", - "The field {0} must match the regular expression '{1}'.": "Поле {0} должно соответствовать регулярному выражению '{1}'.", + "The field {0} must match the regular expression '{1}'.": "Поле {0} не соответствует запрошенному формату.", "The {0} field is required.": "Поле {0} необходимо заполнить.", "The field {0} must be a string with a maximum length of {1}.": "Поле {0} должно быть строкой с максимальной длиной {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Поле {0} должно быть строкой с минимальной длиной {2} и максимальной длиной {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json index 447495e236..a70a3cd329 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "{0} alanı en az '{1}' uzunluğunda bir metin ya da dizi olmalıdır.", "The {0} field is not a valid phone number.": "{0} alanı geçerli bir telefon numarası olmalıdır.", "The field {0} must be between {1} and {2}.": "{0} değeri {1} ile {2} arasında olmalıdır.", - "The field {0} must match the regular expression '{1}'.": "{0} alanı şu düzenli ifadeye uymalıdır: '{1}'.", + "The field {0} must match the regular expression '{1}'.": "{0} alanı istenilen biçimde değil.", "The {0} field is required.": "{0} alanı zorunludur.", "The field {0} must be a string with a maximum length of {1}.": "{0} alanı en fazla {1} uzunluğunda bir metin olmalıdır.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "{0} alanı en az {2}, en fazla {1} uzunluğunda bir metin olmalıdır.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json index 2d9c0c55f5..9f8e3e30ee 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json @@ -9,7 +9,7 @@ "The field {0} must be a string or array type with a maximum length of '{1}'.": "Trường {0} phải là một chuỗi hoặc một mảng với độ dài tối đa là '{1}'.", "The {0} field is not a valid phone number.": "Trường {0} không phải là một số điện thoại hợp lệ", "The field {0} must be between {1} and {2}.": "Trường {0} phải ở giữa {1} and {2}.", - "The field {0} must match the regular expression '{1}'.": "Trường {0} phải khớp với biểu thức chính quy '{1}'.", + "The field {0} must match the regular expression '{1}'.": "Trường {0} không khớp với định dạng được yêu cầu.", "The {0} field is required.": "Trường {0} là bắt buộc.", "The field {0} must be a string with a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối đa là {1}.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối thiểu {2} và tối đa là {1}.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json index 282423a44a..88a6e93acf 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "字段{0}必须是最小长度为'{1}'的字符串或数组.", "The {0} field is not a valid phone number.": "字段{0}不是有效的手机号码.", "The field {0} must be between {1} and {2}.": "字段{0}值必须在{1}和{2}范围内.", - "The field {0} must match the regular expression '{1}'.": "字段{0}必须匹配正则表达式'{1}'.", + "The field {0} must match the regular expression '{1}'.": "字段{0}与请求的格式不匹配。", "The {0} field is required.": "字段{0}不可为空.", "The field {0} must be a string with a maximum length of {1}.": "字段{0}必须是长度为{1}的字符串.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "字段{0}必须是最小长度为{2}并且最大长度{1}的字符串.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json index 1e7e419625..2cd200151c 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json @@ -10,7 +10,7 @@ "The field {0} must be a string or array type with a minimum length of '{1}'.": "欄位{0}必須是最小長度為'{1}'的字串或陣列.", "The {0} field is not a valid phone number.": "欄位{0}不是有效的電話號碼.", "The field {0} must be between {1} and {2}.": "欄位{0}值必須在{1}和{2}範圍內.", - "The field {0} must match the regular expression '{1}'.": "欄位{0}必須匹配正規表示式'{1}'.", + "The field {0} must match the regular expression '{1}'.": "字段{0}与请求的格式不匹配。", "The {0} field is required.": "欄位{0}不可為空.", "The field {0} must be a string with a maximum length of {1}.": "欄位{0}必須是長度為{1}的字串.", "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "欄位{0}必須是最小長度為{2}並且最大長度{1}的字串.", diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs index 7d3505d616..8a6d7d57a1 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs @@ -18,6 +18,14 @@ namespace Microsoft.Extensions.FileProviders return fileInfo.ReadAsString(Encoding.UTF8); } + /// + /// Reads file content as string using encoding. + /// + public static Task ReadAsStringAsync([NotNull] this IFileInfo fileInfo) + { + return fileInfo.ReadAsStringAsync(Encoding.UTF8); + } + /// /// Reads file content as string using the given . /// @@ -34,6 +42,22 @@ namespace Microsoft.Extensions.FileProviders } } + /// + /// Reads file content as string using the given . + /// + public static async Task ReadAsStringAsync([NotNull] this IFileInfo fileInfo, Encoding encoding) + { + Check.NotNull(fileInfo, nameof(fileInfo)); + + using (var stream = fileInfo.CreateReadStream()) + { + using (var streamReader = new StreamReader(stream, encoding, true)) + { + return await streamReader.ReadToEndAsync(); + } + } + } + /// /// Reads file content as byte[]. /// diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl.json b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl-PL.json similarity index 79% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl.json rename to framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl-PL.json index c794c9c123..5370ccdf57 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl.json +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "BirthDate": "Data urodzenia", "Value1": "Wartość jeden" diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs index 28a98bac8e..f9fcc878b1 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs @@ -11,16 +11,20 @@ namespace Volo.Abp.Auditing.App.Entities } - public AppEntityWithDisableAuditingAndPropertyHasAudited(Guid id, string name, string name2) + public AppEntityWithDisableAuditingAndPropertyHasAudited(Guid id, string name, string name2, string name3) : base(id) { Name = name; Name2 = name2; + Name3 = name3; } [Audited] public string Name { get; set; } public string Name2 { get; set; } + + [Audited] + public string Name3 { get; set; } } } diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index 502ba6dcc8..2887eb8510 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -7,6 +7,7 @@ using NSubstitute; using Volo.Abp.Auditing.App.Entities; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.Auditing @@ -15,10 +16,12 @@ namespace Volo.Abp.Auditing { private IAuditingStore _auditingStore; private IAuditingManager _auditingManager; + private IUnitOfWorkManager _unitOfWorkManager; public Auditing_Tests() { _auditingManager = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); } protected override void AfterAddApplication(IServiceCollection services) @@ -158,18 +161,46 @@ namespace Volo.Abp.Auditing using (var scope = _auditingManager.BeginScope()) { var repository = ServiceProvider.GetRequiredService>(); - await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(Guid.NewGuid(), "test name", "test name2")); + await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(Guid.NewGuid(), "test name", "test name2", "test name3")); await scope.SaveAsync(); } #pragma warning disable 4014 _auditingStore.Received().SaveAsync(Arg.Is(x => - x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 1 && - x.EntityChanges[0].PropertyChanges[0].PropertyName == - nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name))); + x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 2 && + x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name) && + x.EntityChanges[0].PropertyChanges[1].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name3))); #pragma warning restore 4014 } + [Fact] + public virtual async Task Should_Write_AuditLog_For_Entity_That_Property_Has_Audited_Attribute_And_Has_Changed_Even_Entity_Has_DisableAuditing_Attribute() + { + var entityId = Guid.NewGuid(); + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(entityId, "test name", "test name2", "test name3")); + + using (var scope = _auditingManager.BeginScope()) + { + using (var uow = _unitOfWorkManager.Begin()) + { + var entity = await repository.GetAsync(entityId); + entity.Name = "new name1"; + + await repository.UpdateAsync(entity); + + await uow.CompleteAsync(); + } + + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + _auditingStore.Received().SaveAsync(Arg.Is(x => + x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 1 && + x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name))); +#pragma warning restore 4014 + } [Fact] public virtual async Task Should_Write_AuditLog_If_There_No_Action_And_No_EntityChanges() diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj index a86ea9f051..030eddb65a 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj @@ -7,13 +7,6 @@ - - - - - - - diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs index e4cdfe0556..7f1175bcbb 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs @@ -1,6 +1,4 @@ using Volo.Abp.Autofac; -using Volo.Abp.Emailing.Localization; -using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -18,14 +16,6 @@ namespace Volo.Abp.Emailing { options.FileSets.AddEmbedded(); }); - - Configure(options => - { - options.Resources - .Add() - .AddVirtualJson("/Volo/Abp/Emailing/Localization"); - }); - } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs deleted file mode 100644 index 24d7298f6c..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Shouldly; -using Volo.Abp.Emailing.Templates; -using Volo.Abp.Testing; -using Xunit; - -namespace Volo.Abp.Emailing -{ - public class EmailTemplateRender_Tests : AbpIntegratedTest - { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } - - private readonly ITemplateRender _templateRender; - - public EmailTemplateRender_Tests() - { - _templateRender = GetRequiredService(); - } - - [Fact] - public async Task RenderAsync() - { - var template = "Hello {{email}} {{ for order in orders }}{{ order.id }}:{{ order.name }},{{ end }}"; - - var model = new ModelClass - { - Email = "john@abp.io", - Orders = new List - { - new ModelClass.Order - { - Id = "1", - Name = "iphone" - }, - new ModelClass.Order - { - Id = "2", - Name = "ipad" - } - } - }; - - var result = await _templateRender.RenderAsync(template, model); - result.ShouldBe("Hello john@abp.io 1:iphone,2:ipad,"); - } - - public class ModelClass - { - public string Email { get; set; } - - public List Orders { get; set; } - - public class Order - { - public string Id { get; set; } - - public string Name { get; set; } - } - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs deleted file mode 100644 index 1e4ea8c090..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading.Tasks; -using Shouldly; -using Volo.Abp.Emailing.Templates; -using Volo.Abp.Testing; -using Xunit; - -namespace Volo.Abp.Emailing -{ - public class EmailTemplateStore_Tests : AbpIntegratedTest - { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } - - private readonly IEmailTemplateProvider _emailTemplateProvider; - - public EmailTemplateStore_Tests() - { - _emailTemplateProvider = GetRequiredService(); - } - - [Fact] - public async Task Should_Get_Registered_Template() - { - var template = await _emailTemplateProvider.GetAsync("template1", "tr"); - template.Content.ShouldContain("Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın."); - } - - [Fact] - public async Task Should_Get_Default_Culture_Template() - { - var template = await _emailTemplateProvider.GetAsync("template1", "zh-Hans"); - template.Content.ShouldContain("Please confirm your email address by clicking the link below."); - } - - [Fact] - public async Task Should_Get_Registered_Template_With_Layout() - { - var template = await _emailTemplateProvider.GetAsync("template2", "en"); - - template.Content.ShouldContain($"{Environment.NewLine} " + "Please confirm your email address by clicking the link below."); - } - - - [Fact] - public async Task Should_Get_Registered_Template_With_Localize() - { - var template = await _emailTemplateProvider.GetAsync("template3", "tr"); - template.Content.ShouldContain("Merhaba Abp"); - } - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs deleted file mode 100644 index bdc75f9f2f..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Volo.Abp.Emailing.Localization -{ - public class AbpEmailingTestResource - { - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json deleted file mode 100644 index 5f6e35488e..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "cs", - "texts": { - "hello": "ahoj" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl-PL.json similarity index 63% rename from framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json rename to framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl-PL.json index 6dd6654aa1..3b4ee39a1e 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "hello": "witaj" } diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json deleted file mode 100644 index 0e33dee138..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pt-BR", - "texts": { - "hello": "Olá" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json deleted file mode 100644 index 8261599d78..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "vi", - "texts": { - "hello": "xin chào" - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json deleted file mode 100644 index dd12964f70..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "zh-Hant", - "texts": { - "hello": "哈囉" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs deleted file mode 100644 index 60c9a2ccf3..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Volo.Abp.Emailing.Localization; -using Volo.Abp.Emailing.Templates; -using Volo.Abp.Emailing.Templates.VirtualFiles; - -namespace Volo.Abp.Emailing -{ - public class TestEmailTemplateProvider : EmailTemplateDefinitionProvider - { - public override void Define(IEmailTemplateDefinitionContext context) - { - var template1 = new EmailTemplateDefinition("template1", defaultCultureName: "en", layout: null) - .AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template1"); - context.Add(template1); - - var template2 = new EmailTemplateDefinition("template2", layout: StandardEmailTemplates.DefaultLayout) - .AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template2"); - context.Add(template2); - - var template3 = new EmailTemplateDefinition("template3", layout: null, singleTemplateFile: true, localizationResource: typeof(AbpEmailingTestResource)) - .AddTemplateVirtualFile("/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl"); - context.Add(template3); - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl deleted file mode 100644 index 49a951e8c0..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl +++ /dev/null @@ -1,4 +0,0 @@ -Please confirm your email address by clicking the link below. -We may need to send you critical information about our service and it is important that we have an accurate email address. - -Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl deleted file mode 100644 index 3e572aedbb..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl +++ /dev/null @@ -1,4 +0,0 @@ -Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın. -Size hizmetimizle ilgili kritik bilgileri göndermemiz gerekebilir ve doğru bir e-posta adresimizin olması önemlidir. - -E-posta adresini onayla \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl deleted file mode 100644 index 49a951e8c0..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl +++ /dev/null @@ -1,4 +0,0 @@ -Please confirm your email address by clicking the link below. -We may need to send you critical information about our service and it is important that we have an accurate email address. - -Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl deleted file mode 100644 index f30f512848..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl +++ /dev/null @@ -1 +0,0 @@ -{{#L:hello}} Abp \ No newline at end of file diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs deleted file mode 100644 index 4b31554637..0000000000 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Localization; -using Shouldly; -using Volo.Abp.Localization.TestResources.Source; -using Volo.Abp.Modularity; -using Volo.Abp.Testing; -using Volo.Abp.VirtualFileSystem; -using Xunit; - -namespace Volo.Abp.Localization -{ - public class TemplateLocalizer_Tests : AbpIntegratedTest - { - private readonly ITemplateLocalizer _templateLocalizer; - private readonly IStringLocalizer _testResource; - - public TemplateLocalizer_Tests() - { - _testResource = GetRequiredService>(); - _templateLocalizer = GetRequiredService(); - } - - [Fact] - public void Should_Localize() - { - using (CultureHelper.Use("en")) - { - _templateLocalizer.Localize(_testResource, "

{{#L:CarPlural}} {{#L:Universe}}

") - .ShouldBe("

Cars Universe

"); - } - } - - [Fact] - public void Should_Work_Even_If_No_Text_To_Localize() - { - using (CultureHelper.Use("en")) - { - _templateLocalizer.Localize(_testResource, "

test

") - .ShouldBe("

test

"); - } - } - - [DependsOn(typeof(AbpTestBaseModule))] - [DependsOn(typeof(AbpLocalizationModule))] - public class TestModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.FileSets.AddEmbedded(); - }); - - Configure(options => - { - options.Resources - .Add("en") - .AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); - }); - } - } - } -} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl-PL.json similarity index 79% rename from framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl.json rename to framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl-PL.json index 5e343cf2f9..a25b79d6d8 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "USA": "Stany Zjednoczone Ameryki", "Brazil": "Brazylia" diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl-PL.json similarity index 86% rename from framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl.json rename to framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl-PL.json index 1d0bfdf66a..cce4cfd722 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "ThisFieldIsRequired": "To pole jest wymagane", "MaxLenghtErrorMessage": "To pole może mieć maksymalnie '{0}' znaków" diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl-PL.json similarity index 92% rename from framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl.json rename to framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl-PL.json index d014bc7b30..e208fb12b3 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Hello {0}.": "Witaj {0}.", "Car": "Samochód", diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl-PL.json similarity index 69% rename from framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl.json rename to framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl-PL.json index 2935790010..5b754a7ae1 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "SeeYou": "Do zobaczenia" } diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj new file mode 100644 index 0000000000..0d230552e8 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj @@ -0,0 +1,25 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs new file mode 100644 index 0000000000..294596c693 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Options; +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating +{ + public class AbpTextTemplatingOptions_Tests : AbpTextTemplatingTestBase + { + private readonly AbpTextTemplatingOptions _options; + + public AbpTextTemplatingOptions_Tests() + { + _options = GetRequiredService>().Value; + } + + [Fact] + public void Should_Auto_Add_TemplateDefinitionProviders_To_Options() + { + _options + .DefinitionProviders + .ShouldContain(typeof(TestTemplateDefinitionProvider)); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs new file mode 100644 index 0000000000..ca5dc20445 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.TextTemplating +{ + public abstract class AbpTextTemplatingTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs new file mode 100644 index 0000000000..8e8b69e116 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs @@ -0,0 +1,32 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.TextTemplating.Localization; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TextTemplating +{ + [DependsOn( + typeof(AbpTextTemplatingModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule), + typeof(AbpLocalizationModule) + )] + public class AbpTextTemplatingTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded("Volo.Abp.TextTemplating"); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/Localization"); + }); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs new file mode 100644 index 0000000000..a4e53e3861 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.TextTemplating.Localization +{ + public class TestLocalizationSource + { + } +} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json similarity index 52% rename from framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json rename to framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json index 38f7cf7e5b..a0dff7e930 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json @@ -1,6 +1,6 @@ { "culture": "en", "texts": { - "hello": "hello" - } + "HelloText": "Hello" + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json similarity index 50% rename from framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json rename to framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json index 6c3c94cfdf..d09f02cd8f 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json @@ -1,6 +1,6 @@ { "culture": "tr", "texts": { - "hello": "Merhaba" - } + "HelloText": "Merhaba" + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl new file mode 100644 index 0000000000..8bcab86d0f --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl @@ -0,0 +1 @@ +{{L "HelloText"}}. Please click to the following link to get an email to reset your password! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl new file mode 100644 index 0000000000..a780e210b0 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl @@ -0,0 +1 @@ +*BEGIN*{{content}}*END* \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl new file mode 100644 index 0000000000..1746eed52b --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl @@ -0,0 +1 @@ +Welcome {{model.name}} to the abp.io! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl new file mode 100644 index 0000000000..581016bc4d --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl @@ -0,0 +1 @@ +Merhaba {{model.name}}, abp.io'ya hoşgeldiniz! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs new file mode 100644 index 0000000000..51c78cc13e --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs @@ -0,0 +1,41 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateDefinitionTests : AbpTextTemplatingTestBase + { + private readonly ITemplateDefinitionManager _templateDefinitionManager; + + public TemplateDefinitionTests() + { + _templateDefinitionManager = GetRequiredService(); + } + + [Fact] + public void Should_Retrieve_Template_Definition_By_Name() + { + var welcomeEmailTemplate = _templateDefinitionManager.Get(TestTemplates.WelcomeEmail); + welcomeEmailTemplate.Name.ShouldBe(TestTemplates.WelcomeEmail); + welcomeEmailTemplate.IsInlineLocalized.ShouldBeFalse(); + + var forgotPasswordEmailTemplate = _templateDefinitionManager.Get(TestTemplates.ForgotPasswordEmail); + forgotPasswordEmailTemplate.Name.ShouldBe(TestTemplates.ForgotPasswordEmail); + forgotPasswordEmailTemplate.IsInlineLocalized.ShouldBeTrue(); + } + + [Fact] + public void Should_Get_Null_If_Template_Not_Found() + { + var definition = _templateDefinitionManager.GetOrNull("undefined-template"); + definition.ShouldBeNull(); + } + + [Fact] + public void Should_Retrieve_All_Template_Definitions() + { + var definitions = _templateDefinitionManager.GetAll(); + definitions.Count.ShouldBeGreaterThan(1); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs new file mode 100644 index 0000000000..14b3df3486 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateRenderer_Tests : AbpTextTemplatingTestBase + { + private readonly ITemplateRenderer _templateRenderer; + + public TemplateRenderer_Tests() + { + _templateRenderer = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Different_Cultures() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "tr" + )).ShouldBe("Merhaba John, abp.io'ya hoşgeldiniz!"); + + //"en-US" fallbacks to "en" since "en-US" doesn't exists and "en" is the fallback culture + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "en-US" + )).ShouldBe("Welcome John to the abp.io!"); + + //"fr" fallbacks to "en" since "fr" doesn't exists and "en" is the default culture + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + Name = "John" //Intentionally written as PascalCase since Scriban supports it + }, + cultureName: "fr" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Stronly_Typed_Model() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new WelcomeEmailModel("John"), + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Dictionary_Model() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new Dictionary() { { "name", "John" } }, + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Inline_Localized_Template() + { + (await _templateRenderer.RenderAsync( + TestTemplates.ForgotPasswordEmail, + cultureName: "en" + )).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*"); + + (await _templateRenderer.RenderAsync( + TestTemplates.ForgotPasswordEmail, + cultureName: "tr" + )).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*"); + } + + private class WelcomeEmailModel + { + public string Name { get; set; } + + public WelcomeEmailModel() + { + + } + + public WelcomeEmailModel(string name) + { + Name = name; + } + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs new file mode 100644 index 0000000000..05df1378b2 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs @@ -0,0 +1,32 @@ +using Volo.Abp.TextTemplating.Localization; + +namespace Volo.Abp.TextTemplating +{ + public class TestTemplateDefinitionProvider : TemplateDefinitionProvider + { + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition( + TestTemplates.WelcomeEmail, + defaultCultureName: "en" + ).WithVirtualFilePath("/SampleTemplates/WelcomeEmail") + ); + + context.Add( + new TemplateDefinition( + TestTemplates.ForgotPasswordEmail, + localizationResource: typeof(TestLocalizationSource), + layout: TestTemplates.TestTemplateLayout1 + ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail.tpl") + ); + + context.Add( + new TemplateDefinition( + TestTemplates.TestTemplateLayout1, + isLayout: true + ).WithVirtualFilePath("/SampleTemplates/TestTemplateLayout1.tpl") + ); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs new file mode 100644 index 0000000000..a2b605c213 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.TextTemplating +{ + public static class TestTemplates + { + public const string WelcomeEmail = "WelcomeEmail"; + public const string ForgotPasswordEmail = "ForgotPasswordEmail"; + public const string TestTemplateLayout1 = "TestTemplateLayout1"; + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs new file mode 100644 index 0000000000..da2d4179de --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + //TODO: Make tests running again! + //public class VirtualFileTemplateContributor_Tests : AbpTextTemplatingTestBase + //{ + // [Fact] + // public async Task Should_Get_Localized_Content_By_Culture() + // { + // var contributor = new VirtualFileTemplateContentContributor( + // "/SampleTemplates/WelcomeEmail" + // ); + + // contributor.Initialize( + // new TemplateContentContributorInitializationContext( + // new TemplateDefinition("Test"), + // ServiceProvider + // ) + // ); + + // (await contributor + // .GetOrNullAsync("en")).ShouldBe("Welcome {{model.name}} to the abp.io!"); + + // (await contributor + // .GetOrNullAsync("tr")).ShouldBe("Merhaba {{model.name}}, abp.io'ya hoşgeldiniz!"); + // } + + // [Fact] + // public async Task Should_Get_Non_Localized_Template_Content() + // { + // var contributor = new VirtualFileTemplateContentContributor( + // "/SampleTemplates/ForgotPasswordEmail.tpl" + // ); + + // contributor.Initialize( + // new TemplateContentContributorInitializationContext( + // new TemplateDefinition("Test"), + // ServiceProvider + // ) + // ); + + // (await contributor + // .GetOrNullAsync()).ShouldBe("{{l \"HelloText\"}}. Please click to the following link to get an email to reset your password!"); + // } + //} +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json similarity index 98% rename from modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl.json rename to modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json index b661af8050..032ac41cfa 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "UserName": "Nazwa użytkownika", "EmailAddress": "Adres email", diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index e194c1544d..60f55497da 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -29,12 +29,12 @@ namespace Volo.Abp.Account.Web.Pages.Account public IdentityServerSupportedLoginModel( IAuthenticationSchemeProvider schemeProvider, - IOptions accountOptions, - IIdentityServerInteractionService interaction, - IClientStore clientStore, + IOptions accountOptions, + IIdentityServerInteractionService interaction, + IClientStore clientStore, IEventService identityServerEvents) :base( - schemeProvider, + schemeProvider, accountOptions) { Interaction = interaction; @@ -95,7 +95,6 @@ namespace Volo.Abp.Account.Web.Pages.Account return Page(); } - [UnitOfWork] //TODO: Will be removed when we implement action filter public override async Task OnPostAsync(string action) { if (action == "Cancel") @@ -116,7 +115,7 @@ namespace Volo.Abp.Account.Web.Pages.Account ValidateModel(); ExternalProviders = await GetExternalProviders(); - + EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin); await ReplaceEmailToUsernameOfInputIfNeeds(); @@ -166,7 +165,6 @@ namespace Volo.Abp.Account.Web.Pages.Account return RedirectSafely(ReturnUrl, ReturnUrlHash); } - [UnitOfWork] public override async Task OnPostExternalLogin(string provider) { if (AccountOptions.WindowsAuthenticationSchemeName == provider) diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl.json b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl-PL.json similarity index 93% rename from modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl.json rename to modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl-PL.json index 8806854643..05ba8451c6 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl.json +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Permission:Blogging": "Blog", "Permission:Blogs": "Blogi", diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl.json b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl-PL.json similarity index 98% rename from modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl.json rename to modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl-PL.json index 15e0c7eb0d..3795ab93d3 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl.json +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:Blogs": "Blogi", "Menu:BlogManagement": "Zarządzanie blogiem", diff --git a/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl.json b/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl-PL.json similarity index 91% rename from modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl.json rename to modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl-PL.json index f131f9e139..ca06e49ef5 100644 --- a/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl.json +++ b/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "WelcomeVoloDocs": "Witaj w VoloDocs!", "NoProjectWarning": "Nie ma jeszcze zdefiniowanego projektu!", diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl.json b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl-PL.json similarity index 98% rename from modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl.json rename to modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl-PL.json index be91b22800..80922afc71 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl.json +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Permission:DocumentManagement": "Zarządzanie dokumentacją", "Permission:Projects": "Projekty", diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs index 02a7c171bd..430cd9ead2 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs @@ -7,8 +7,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Nest; -using Newtonsoft.Json; using Volo.Abp; using Volo.Abp.Caching; using Volo.Docs.Caching; diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs index cc0f03a60a..f97eddf0e2 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs @@ -87,6 +87,11 @@ namespace Volo.Docs.FileSystem.Documents { throw new SecurityException("Can not get a resource file out of the project folder!"); } + + if (!File.Exists(path)) + { + throw new DocumentNotFoundException(path); + } } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index a6285389de..ef6f6e1ebb 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -48,68 +48,155 @@ namespace Volo.Docs.GitHub.Documents fileName = documentName.Substring(documentName.LastIndexOf('/') + 1); } - var fileCommits = await GetFileCommitsAsync(project, version, project.GetGitHubInnerUrl(languageCode, documentName)); - - var documentCreationTime = fileCommits.LastOrDefault()?.Commit.Author.Date.DateTime ?? DateTime.MinValue; - - var lastSignificantUpdateTime = !isNavigationDocument && !isParameterDocument && version == project.LatestVersionBranchName ? - await GetLastSignificantUpdateTime( - fileCommits, - project, - project.GetGitHubInnerUrl(languageCode, documentName), - lastKnownSignificantUpdateTime, - documentCreationTime - ) ?? lastKnownSignificantUpdateTime - : null; + var content = await DownloadWebContentAsStringAsync(rawDocumentUrl, token, userAgent); + var commits = await GetGitHubCommitsOrNull(project, documentName, languageCode, version); + + var documentCreationTime = GetFirstCommitDate(commits); + var lastUpdateTime = GetLastCommitDate(commits); + var lastSignificantUpdateTime = await GetLastKnownSignificantUpdateTime(project, documentName, languageCode, version, lastKnownSignificantUpdateTime, isNavigationDocument, isParameterDocument, commits, documentCreationTime); - var document = new Document(GuidGenerator.Create(), + var document = new Document + ( + GuidGenerator.Create(), project.Id, documentName, version, languageCode, fileName, - await DownloadWebContentAsStringAsync(rawDocumentUrl, token, userAgent), + content, project.Format, editLink, rootUrl, rawRootUrl, localDirectory, documentCreationTime, - fileCommits.FirstOrDefault()?.Commit.Author.Date.DateTime ?? DateTime.MinValue, + lastUpdateTime, DateTime.Now, - lastSignificantUpdateTime); + lastSignificantUpdateTime + ); + + if (isNavigationDocument || isParameterDocument) + { + return document; + } + + var authors = GetAuthors(commits); + foreach (var author in authors) + { + document.AddContributor(author.Login, author.HtmlUrl, author.AvatarUrl); + } - var authors = fileCommits + return document; + } + + private async Task GetLastKnownSignificantUpdateTime( + Project project, + string documentName, + string languageCode, + string version, + DateTime? lastKnownSignificantUpdateTime, + bool isNavigationDocument, + bool isParameterDocument, + IReadOnlyList commits, + DateTime documentCreationTime) + { + return !isNavigationDocument && !isParameterDocument && version == project.LatestVersionBranchName + ? await GetLastSignificantUpdateTime( + commits, + project, + project.GetGitHubInnerUrl(languageCode, documentName), + lastKnownSignificantUpdateTime, + documentCreationTime + ) ?? lastKnownSignificantUpdateTime + : null; + } + + private static List GetAuthors(IReadOnlyList commits) + { + if (commits == null || !commits.Any()) + { + return new List(); + } + + return commits .Where(x => x.Author != null) .Select(x => x.Author) .GroupBy(x => x.Id) .OrderByDescending(x => x.Count()) - .Select(x => x.FirstOrDefault()).ToList(); + .Select(x => x.FirstOrDefault()) + .ToList(); + } + + private static DateTime GetLastCommitDate(IReadOnlyList commits) + { + return GetCommitDate(commits, false); + } - if (!isNavigationDocument && !isParameterDocument) + private static DateTime GetFirstCommitDate(IReadOnlyList commits) + { + return GetCommitDate(commits, true); + } + + private static DateTime GetCommitDate(IReadOnlyList commits, bool isFirstCommit) + { + if (commits == null) { - foreach (var author in authors) - { - document.AddContributor(author.Login, author.HtmlUrl, author.AvatarUrl); - } + return DateTime.MinValue; } - return document; + var gitHubCommit = isFirstCommit ? + commits.LastOrDefault() : //first commit + commits.FirstOrDefault(); //last commit + + if (gitHubCommit == null) + { + return DateTime.MinValue; + } + + if (gitHubCommit.Commit == null) + { + return DateTime.MinValue; + } + + if (gitHubCommit.Commit.Author == null) + { + return DateTime.MinValue; + } + + return gitHubCommit.Commit.Author.Date.DateTime; + } + + private async Task> GetGitHubCommitsOrNull(Project project, string documentName, string languageCode, string version) + { + /* + * Getting file commits usually throws "Resource temporarily unavailable" or "Network is unreachable" + * This is a trival information and running this inside try-catch is safer. + */ + + try + { + return await GetFileCommitsAsync(project, version, project.GetGitHubInnerUrl(languageCode, documentName)); + } + catch (Exception e) + { + Logger.LogError(e.ToString()); + return null; + } } private async Task GetLastSignificantUpdateTime( - IReadOnlyList fileCommits, + IReadOnlyList commits, Project project, string fileName, DateTime? lastKnownSignificantUpdateTime, DateTime documentCreationTime) { - if (!fileCommits.Any()) + if (commits == null || !commits.Any()) { return null; } - var fileCommitsAfterCreation = fileCommits.Take(fileCommits.Count - 1); + var fileCommitsAfterCreation = commits.Take(commits.Count - 1); var commitsToEvaluate = (lastKnownSignificantUpdateTime != null ? fileCommitsAfterCreation.Where(c => c.Commit.Author.Date.DateTime > lastKnownSignificantUpdateTime) @@ -117,7 +204,6 @@ namespace Volo.Docs.GitHub.Documents foreach (var gitHubCommit in commitsToEvaluate) { - var fullCommit = await _githubRepositoryManager.GetSingleCommitsAsync( GetOwnerNameFromUrl(project.GetGitHubUrl()), GetRepositoryNameFromUrl(project.GetGitHubUrl()), diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json index 31fdf8f8f2..67227c506a 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json @@ -22,6 +22,7 @@ "Projects": "Project(s)", "NoProjectWarning": "There are no projects yet!", "DocumentNotFound": "Oops, the requested document was not found!", + "ProjectNotFound": "Oops, the requested project was not found!", "NavigationDocumentNotFound": "This version does not have a navigation document!", "DocumentNotFoundInSelectedLanguage": "Document in the language you wanted is not found. Document in the default language is shown.", "FilterTopics": "Filter topics", diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl-PL.json similarity index 95% rename from modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl.json rename to modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl-PL.json index 5ba3dd6c10..44bd398458 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Documents": "Dokumenty", "BackToWebsite": "Powrót do strony", diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json index ef4e95f3dc..8f3a3e44fb 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json @@ -1,6 +1,6 @@ { "culture": "zh-Hans", - "texts": { + "texts": { "Documents": "文档", "BackToWebsite": "返回主网站", "Contributors": "贡献者", @@ -14,6 +14,7 @@ "Projects": "项目", "NoProjectWarning": "还没有项目!", "DocumentNotFound": "找不到请求的文档!", + "ProjectNotFound": "找不到请求的项目!", "NavigationDocumentNotFound": "这个版本没有导航文件!", "DocumentNotFoundInSelectedLanguage": "本文档不适用于所选语言, 将以默认语言显示文档.", "FilterTopics": "过滤主题", diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json index 6a2f4d8799..dea171863c 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json @@ -14,6 +14,7 @@ "Projects": "專案", "NoProjectWarning": "沒有專案!", "DocumentNotFound": "找不到要求的文件!", + "ProjectNotFound": "找不到請求的項目!", "NavigationDocumentNotFound": "這個版本沒有導覽文件!", "DocumentNotFoundInSelectedLanguage": "本文件不適用於所選語系,將以預設語系顯示.", "FilterTopics": "過濾主題", diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/HtmlNormalizer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/HtmlNormalizer.cs index 6f0a680ce2..1689a209fe 100644 --- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/HtmlNormalizer.cs +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/HtmlNormalizer.cs @@ -35,5 +35,51 @@ namespace Volo.Docs.HtmlConverting { return Regex.Replace(content, "", "", RegexOptions.IgnoreCase); } + + /// + /// Wraps an image with a tag that's clickable to open the image in a new browser tab. + /// + public static string WrapImagesWithinAnchors(string html) + { + try + { + return Regex.Replace(html, "", match => + { + var link = match.Groups[1].Value; + var imgTag = match.Groups[0].Value; + var title = GetTitleFromTag(imgTag); + + return $"\"{title}\""; + }); + + } + catch + { + // ignored + return html; + } + } + + private static string GetTitleFromTag(string imgTag) + { + if (string.IsNullOrWhiteSpace(imgTag)) + { + return null; + } + + var match = Regex.Match(imgTag, @"\stitle\s?\=\s?(\""|')(.+?)(\""|')", RegexOptions.Multiline); + if (match.Success && match.Groups.Count > 2) + { + return match.Groups[2].ToString().Trim(); + } + + match = Regex.Match(imgTag, @"\salt\s*\=\s*(\""|')(.*?)(\""|')", RegexOptions.Multiline); + if (match.Success && match.Groups.Count > 2) + { + return match.Groups[2].ToString().Trim(); + } + + return null; + } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs index 12dec9b304..9942c52309 100644 --- a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs +++ b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs @@ -44,7 +44,10 @@ namespace Volo.Docs.Markdown languageCode ); - return _markdownConverter.ConvertToHtml(content); + var html = _markdownConverter.ConvertToHtml(content); + + return html; + // return HtmlNormalizer.WrapImagesWithinAnchors(html); } protected virtual string NormalizeLinks( diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml index b7db40f1fe..7f521de34b 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml @@ -5,12 +5,12 @@ @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Clipboard @using Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Popper -@using Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs @using Volo.Abp.AspNetCore.Mvc.UI.Theming @using Volo.Docs @using Volo.Docs.Localization @using Volo.Docs.Pages.Documents.Project +@using Volo.Docs.Pages.Documents.Shared.ErrorComponent @inject IThemeManager ThemeManager @inject IPageLayout PageLayout @inject IHtmlLocalizer L @@ -39,9 +39,10 @@ + } -@if (Model.DocumentFound) +@if (Model.LoadSuccess) {
@@ -152,11 +153,11 @@
-
@@ -171,11 +172,11 @@ - @@ -225,8 +226,8 @@ @if (!string.IsNullOrEmpty(Model.Document.EditLink)) { - - @(L["Edit"]) + + @(L["Edit"]) (@L["LastEditTime"]: @Model.Document.LastUpdatedTime.ToShortDateString()) } @@ -348,14 +349,31 @@ } else { - @(await Component.InvokeAsync(new - { - model = new DocumentNotFoundPageModel + if (!Model.ProjectFound) + { + @(await Component.InvokeAsync(new + { + model = new ErrorPageModel + { + RedirectUrl = Model.DocumentsUrlPrefix, + ErrorCode = "404", + ErrorMessage = L.GetString("ProjectNotFound") + } + })) + } + + if (!Model.DocumentFound) + { + @(await Component.InvokeAsync(new { - ProjectName = Model.ProjectName, - DocumentName = Model.DocumentName, - LanguageCode = Model.LanguageCode, - Version = Model.Version, - } - })) -} \ No newline at end of file + model = new ErrorPageModel + { + RedirectUrl = Model.DocumentsUrlPrefix + Model.LanguageCode + "/" + Model.ProjectName + "/" + + (Model.LatestVersionInfo.IsSelected ? DocsAppConsts.Latest : Model.Version), + ErrorCode = "404", + ErrorMessage = L.GetString("DocumentNotFound"), + AutoRedirect = !Model.DocumentName.IsNullOrWhiteSpace() + } + })) + } +} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs index 04f8614f56..0acd12531f 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs @@ -7,13 +7,11 @@ using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Volo.Abp; -using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; using Volo.Abp.Domain.Entities; -using Volo.Abp.Localization; using Volo.Docs.Documents; using Volo.Docs.HtmlConverting; using Volo.Docs.Models; @@ -37,6 +35,10 @@ namespace Volo.Docs.Pages.Documents.Project public bool DocumentFound { get; set; } = true; + public bool ProjectFound { get; set; } = true; + + public bool LoadSuccess => DocumentFound && ProjectFound; + public string DefaultLanguageCode { get; set; } public ProjectDto Project { get; set; } @@ -120,7 +122,8 @@ namespace Volo.Docs.Pages.Documents.Project catch (EntityNotFoundException e) { Logger.LogWarning(e.Message); - return NotFound(); + ProjectFound = false; + return Page(); } if (ShowProjectsCombobox) @@ -344,43 +347,13 @@ namespace Volo.Docs.Pages.Documents.Project try { - if (DocumentName.IsNullOrWhiteSpace()) - { - Document = await _documentAppService.GetDefaultAsync( - new GetDefaultDocumentInput - { - ProjectId = Project.Id, - LanguageCode = LanguageCode, - Version = Version - } - ); - } - else - { - Document = await _documentAppService.GetAsync( - new GetDocumentInput - { - ProjectId = Project.Id, - Name = DocumentNameWithExtension, - LanguageCode = LanguageCode, - Version = Version - } - ); - } + Document = await GetSpecificDocumentOrDefaultAsync(LanguageCode); } catch (DocumentNotFoundException) { if (LanguageCode != DefaultLanguageCode) { - Document = await _documentAppService.GetAsync( - new GetDocumentInput - { - ProjectId = Project.Id, - Name = DocumentNameWithExtension, - LanguageCode = DefaultLanguageCode, - Version = Version - } - ); + Document = await GetSpecificDocumentOrDefaultAsync(DefaultLanguageCode); DocumentLanguageIsDifferent = true; } @@ -427,6 +400,8 @@ namespace Volo.Docs.Pages.Documents.Project Document.LocalDirectory ); + content = HtmlNormalizer.WrapImagesWithinAnchors(content); + //todo find a way to make it on client in prismJS configuration (eg: map C# => csharp) content = HtmlNormalizer.ReplaceCodeBlocksLanguage( content, @@ -542,6 +517,33 @@ namespace Volo.Docs.Pages.Documents.Project } + private async Task GetSpecificDocumentOrDefaultAsync(string languageCode) + { + if (DocumentName.IsNullOrWhiteSpace()) + { + return await _documentAppService.GetDefaultAsync( + new GetDefaultDocumentInput + { + ProjectId = Project.Id, + LanguageCode = languageCode, + Version = Version + } + ); + } + else + { + return await _documentAppService.GetAsync( + new GetDocumentInput + { + ProjectId = Project.Id, + Name = DocumentNameWithExtension, + LanguageCode = languageCode, + Version = Version + } + ); + } + } + public async Task SetDocumentPreferencesAsync() { var projectParameters = await _documentAppService.GetParametersAsync( diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js index 1b3d5ac7c3..71fb550539 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js @@ -203,31 +203,31 @@ 'YandexBot', 'msnbot', 'Rambler', - 'Yahoo', - 'AbachoBOT', - 'accoona', + 'Yahoo', + 'AbachoBOT', + 'accoona', 'AcoiRobot', 'ASPSeek', - 'CrocCrawler', + 'CrocCrawler', 'Dumbot', - 'FAST-WebCrawler', - 'GeonaBot', + 'FAST-WebCrawler', + 'GeonaBot', 'Gigabot', - 'Lycos', - 'MSRBOT', + 'Lycos', + 'MSRBOT', 'Scooter', 'AltaVista', - 'IDBot', - 'eStyle', - 'Scrubby', - 'Slurp', - 'DuckDuckBot', - 'Baiduspider', - 'VoilaBot', - 'ExaLead', - 'Search Dog', - 'MSN Bot' , - 'BingBot' + 'IDBot', + 'eStyle', + 'Scrubby', + 'Slurp', + 'DuckDuckBot', + 'Baiduspider', + 'VoilaBot', + 'ExaLead', + 'Search Dog', + 'MSN Bot', + 'BingBot' ]; var agent = navigator.userAgent; @@ -276,9 +276,8 @@ if (queryStrings.length <= 0) { returnList.push(key + "=" + $(this).val()); - - } - else { + + } else { for (var k = 0; k < queryStrings.length; k++) { returnList.push(key + "=" + $(this).val() + "&" + queryStrings[k]); } @@ -291,12 +290,12 @@ var queryStrings = getQueryStringsFromComboboxes(0); for (var i = 0; i < queryStrings.length; i++) { - html += "" + queryStrings[i]+" " + html += "" + queryStrings[i] + " " } $("#crawler_link").html(html); }; - + initNavigationFilter("sidebar-scroll"); initAnchorTags(".docs-page .docs-body"); @@ -309,5 +308,4 @@ }); -})(jQuery); - +})(jQuery); \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundPageModel.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundPageModel.cs deleted file mode 100644 index 72b2b97584..0000000000 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundPageModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent -{ - public class DocumentNotFoundPageModel - { - public string ProjectName { get; set; } - - public string LanguageCode { get; set; } - - public string Version { get; set; } - - public string DocumentName { get; set; } - - public string DocumentsUrlPrefix { get; set; } - } -} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundViewComponent.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundViewComponent.cs deleted file mode 100644 index 7aeed9beee..0000000000 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundViewComponent.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using Volo.Abp.AspNetCore.Mvc; - -namespace Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent -{ - public class DocumentNotFoundViewComponent : AbpViewComponent - { - private readonly DocsUiOptions _options; - - public DocumentNotFoundViewComponent(IOptions options) - { - _options = options.Value; - } - public IViewComponentResult Invoke(DocumentNotFoundPageModel model, string defaultErrorMessageKey) - { - model.DocumentsUrlPrefix = _options.RoutePrefix; - - return View("~/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml", model); - } - } -} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/Default.cshtml similarity index 55% rename from modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml rename to modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/Default.cshtml index 604ff05da7..4aef93c593 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/Default.cshtml @@ -1,17 +1,19 @@ -@using Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent -@model DocumentNotFoundPageModel +@model Volo.Docs.Pages.Documents.Shared.ErrorComponent.ErrorPageModel @{ }
-
- 404 +
+ @Model.ErrorCode

- "@Model.DocumentName" not found in @Model.ProjectName documents, with @Model.Version version and @Model.LanguageCode language. + @Model.ErrorMessage

-
- - Go Back - +
+ @if (Model.AutoRedirect) + { + + Go Back (3) + + }
@@ -20,7 +22,7 @@ h1 { font-size: 1.75em; line-height: 1.75; - color: #777; + color: #777; font-weight: normal; } @@ -37,7 +39,7 @@ transform: translate(-50%, -50%); text-align: center; } - + .notfound-404 { font-size: 300px; font-weight: 700; @@ -49,5 +51,5 @@ margin-bottom: -150px; z-index: -1; position: relative; - } - \ No newline at end of file + } + diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorPageModel.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorPageModel.cs new file mode 100644 index 0000000000..62a64fc9d6 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorPageModel.cs @@ -0,0 +1,13 @@ +namespace Volo.Docs.Pages.Documents.Shared.ErrorComponent +{ + public class ErrorPageModel + { + public string ErrorMessage { get; set; } + + public string ErrorCode { get; set; } + + public string RedirectUrl { get; set; } + + public bool AutoRedirect { get; set; } = true; + } +} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorViewComponent.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorViewComponent.cs new file mode 100644 index 0000000000..493b6d896a --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/ErrorViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.Docs.Pages.Documents.Shared.ErrorComponent +{ + public class ErrorViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke(ErrorPageModel model) + { + return View("~/Pages/Documents/Shared/ErrorComponent/Default.cshtml", model); + } + } +} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/error.js b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/error.js new file mode 100644 index 0000000000..edc010aa9f --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/ErrorComponent/error.js @@ -0,0 +1,20 @@ +(function ($) { + + $(function () { + var errorPageRedirect = function () { + var second = 3; + var close = setInterval(() => { + $("#ErrorRedirectSeconds").text(`(${--second})`); + if (second === 0) { + clearInterval(close); + $("#ErrorRedirect")[0].click(); + } + }, 1000); + } + + if (document.getElementById("ErrorRedirect")) { + errorPageRedirect(); + } + }); + +})(jQuery); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl-PL.json similarity index 66% rename from modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl.json rename to modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl-PL.json index d714a0d499..86d5f08258 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Features": "Funkcje" } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json similarity index 99% rename from modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl.json rename to modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json index 3a0896147c..54d137988d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:IdentityManagement": "Zarządzanie tożsamością", "Users": "Użytkownicy", diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json similarity index 91% rename from modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl.json rename to modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json index e1043feea7..b552012c1b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Permissions": "Uprawnienia", "OnlyProviderPermissons": "Tylko ten dostawca", diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json similarity index 67% rename from modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl.json rename to modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json index 4aa474d82b..6c29a15453 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Settings": "Ustawienia" } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs index f618011cc7..575aa1eb0c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs @@ -56,6 +56,8 @@ namespace Volo.Abp.TenantManagement await TenantRepository.InsertAsync(tenant); + await CurrentUnitOfWork.SaveChangesAsync(); + using (CurrentTenant.Change(tenant.Id, tenant.Name)) { //TODO: Handle database creation? diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json similarity index 97% rename from modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl.json rename to modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json index 18891c3633..be3af86d22 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:TenantManagement": "Zarządzanie tenantami", "Tenants": "Tenanty", diff --git a/npm/lerna.json b/npm/lerna.json index 71393a4aa2..b6566eea0d 100644 --- a/npm/lerna.json +++ b/npm/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "packages": [ "packs/*" ], diff --git a/npm/ng-packs/lerna.version.json b/npm/ng-packs/lerna.version.json index e750bdad60..ee596b4286 100644 --- a/npm/ng-packs/lerna.version.json +++ b/npm/ng-packs/lerna.version.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "packages": [ "packages/*" ], diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index ae80c4eecb..d141817a6e 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -22,19 +22,19 @@ "generate:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" }, "devDependencies": { - "@abp/ng.account": "^2.6.0", - "@abp/ng.account.config": "^2.6.0", - "@abp/ng.core": "^2.6.0", - "@abp/ng.feature-management": "^2.6.0", - "@abp/ng.identity": "^2.6.0", - "@abp/ng.identity.config": "^2.6.0", - "@abp/ng.permission-management": "^2.6.0", - "@abp/ng.setting-management": "^2.6.0", - "@abp/ng.setting-management.config": "^2.6.0", - "@abp/ng.tenant-management": "^2.6.0", - "@abp/ng.tenant-management.config": "^2.6.0", - "@abp/ng.theme.basic": "^2.6.0", - "@abp/ng.theme.shared": "^2.6.0", + "@abp/ng.account": "~2.6.2", + "@abp/ng.account.config": "~2.6.2", + "@abp/ng.core": "^2.6.2", + "@abp/ng.feature-management": "^2.6.2", + "@abp/ng.identity": "~2.6.2", + "@abp/ng.identity.config": "~2.6.2", + "@abp/ng.permission-management": "^2.6.2", + "@abp/ng.setting-management": "~2.6.2", + "@abp/ng.setting-management.config": "~2.6.2", + "@abp/ng.tenant-management": "~2.6.2", + "@abp/ng.tenant-management.config": "~2.6.2", + "@abp/ng.theme.basic": "~2.6.2", + "@abp/ng.theme.shared": "^2.6.2", "@abp/utils": "^2.6.0", "@angular-builders/jest": "^8.2.0", "@angular-devkit/build-angular": "~0.803.21", @@ -76,7 +76,7 @@ "just-compare": "^1.3.0", "lerna": "^3.19.0", "ng-packagr": "^5.7.1", - "ngxs-reset-plugin": "^1.2.0", + "ngxs-reset-plugin": "^1.3.0", "ngxs-schematic": "^1.1.9", "prettier": "^1.18.2", "protractor": "~5.4.0", diff --git a/npm/ng-packs/packages/account-config/package.json b/npm/ng-packs/packages/account-config/package.json index 214f64e192..9b0c49f810 100644 --- a/npm/ng-packs/packages/account-config/package.json +++ b/npm/ng-packs/packages/account-config/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.account.config", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", diff --git a/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts b/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts index 0add43bc33..d94935c071 100644 --- a/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts +++ b/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts @@ -1,5 +1,6 @@ import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; import { Injectable } from '@angular/core'; +import { eAccountRouteNames } from '@abp/ng.account'; @Injectable({ providedIn: 'root', @@ -7,14 +8,14 @@ import { Injectable } from '@angular/core'; export class AccountConfigService { constructor() { addAbpRoutes({ - name: 'AbpAccount::Menu:Account', + name: eAccountRouteNames.Account, path: 'account', invisible: true, layout: eLayoutType.application, children: [ - { path: 'login', name: 'AbpAccount::Login', order: 1 }, - { path: 'register', name: 'AbpAccount::Register', order: 2 }, - { path: 'manage-profile', name: 'AbpAccount::ManageYourProfile', order: 3 }, + { path: 'login', name: eAccountRouteNames.Login, order: 1 }, + { path: 'register', name: eAccountRouteNames.Register, order: 2 }, + { path: 'manage-profile', name: eAccountRouteNames.ManageProfile, order: 3 }, ], }); } diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index fab129c61d..e61de120fb 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.account", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.config": "^2.6.0", - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.account.config": "^2.6.2", + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/account/src/lib/enums/index.ts b/npm/ng-packs/packages/account/src/lib/enums/index.ts new file mode 100644 index 0000000000..5ac9b6e8fa --- /dev/null +++ b/npm/ng-packs/packages/account/src/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './route-names'; diff --git a/npm/ng-packs/packages/account/src/lib/enums/route-names.ts b/npm/ng-packs/packages/account/src/lib/enums/route-names.ts new file mode 100644 index 0000000000..810985e027 --- /dev/null +++ b/npm/ng-packs/packages/account/src/lib/enums/route-names.ts @@ -0,0 +1,6 @@ +export const enum eAccountRouteNames { + Account = 'AbpAccount::Menu:Account', + Login = 'AbpAccount::Login', + Register = 'AbpAccount::Register', + ManageProfile = 'AbpAccount::ManageYourProfile', +} diff --git a/npm/ng-packs/packages/account/src/public-api.ts b/npm/ng-packs/packages/account/src/public-api.ts index 63cb476f9b..8d1ef3c43a 100644 --- a/npm/ng-packs/packages/account/src/public-api.ts +++ b/npm/ng-packs/packages/account/src/public-api.ts @@ -1,6 +1,6 @@ export * from './lib/account.module'; export * from './lib/components'; -export * from './lib/enums/components'; +export * from './lib/enums'; export * from './lib/tokens'; export * from './lib/models'; export * from './lib/services'; diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index 1249f55cde..e56e0af781 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.core", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", diff --git a/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts b/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts index 1e9dc52bb3..e799eb6784 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts @@ -26,6 +26,7 @@ export class AuthGuard implements CanActivate { return hasValidAccessToken; } - return router.createUrlTree(['/account/login'], { state: { redirectUrl: state.url } }); + router.navigate(['/account/login'], { state: { redirectUrl: state.url } }); + return true; } } diff --git a/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts b/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts index f9c8f45423..b2afab6072 100644 --- a/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts +++ b/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts @@ -10,7 +10,7 @@ import { } from '@ngxs/store'; import clone from 'just-clone'; import snq from 'snq'; -import { ABP } from '../models'; +import { ABP } from '../models/common'; import { getAbpRoutes, organizeRoutes } from '../utils/route-utils'; export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS'); @@ -30,8 +30,8 @@ export class ConfigPlugin implements NgxsPlugin { if (isInitAction && !this.initialized) { const transformedRoutes = transformRoutes(this.router.config); - let { routes } = transformedRoutes; - const { wrappers } = transformedRoutes; + let { routes, wrappers } = transformedRoutes; + wrappers = reduceWrappers(wrappers); routes = organizeRoutes(routes, wrappers); const flattedRoutes = flatRoutes(clone(routes)); @@ -118,3 +118,14 @@ function flatRoutes(routes: ABP.FullRoute[]): ABP.FullRoute[] { return flat(routes); } + +function reduceWrappers(wrappers: ABP.FullRoute[] = []) { + const existingWrappers = new Set(); + + return wrappers.filter(wrapper => { + if (existingWrappers.has(wrapper.name)) return false; + + existingWrappers.add(wrapper.name); + return true; + }); +} diff --git a/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts index 93339bcbcc..1e7f5bf585 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts @@ -10,7 +10,7 @@ describe('AuthGuard', () => { let guard: AuthGuard; const createService = createServiceFactory({ service: AuthGuard, - mocks: [OAuthService], + mocks: [OAuthService, Router], imports: [RouterModule.forRoot([{ path: '', component: RouterOutletComponent }])], declarations: [RouterOutletComponent], providers: [{ provide: APP_BASE_HREF, useValue: '/' }], @@ -26,10 +26,13 @@ describe('AuthGuard', () => { expect(guard.canActivate(null, null)).toBe(true); }); - it('should return url tree when user not logged in', () => { + it('should return navigate to login page with redirectUrl state', () => { const router = spectator.get(Router); - const expectedUrlTree = router.createUrlTree(['/account/login'], { state: { redirectUrl: '/' } }); spectator.get(OAuthService).hasValidAccessToken.andReturn(false); - expect(guard.canActivate(null, { url: '/' } as any) as UrlTree).toEqual(expectedUrlTree); + + expect(guard.canActivate(null, { url: '/' } as any)).toBe(true); + expect(router.navigate).toHaveBeenCalledWith(['/account/login'], { + state: { redirectUrl: '/' }, + }); }); }); diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index f52db27474..cedfcc2be8 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.feature-management", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity-config/package.json b/npm/ng-packs/packages/identity-config/package.json index 40f91f83fd..0a272ed6a9 100644 --- a/npm/ng-packs/packages/identity-config/package.json +++ b/npm/ng-packs/packages/identity-config/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.identity.config", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", diff --git a/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts b/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts index d1b52fed8b..c1fdbb102d 100644 --- a/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts +++ b/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts @@ -1,5 +1,6 @@ import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; import { Injectable } from '@angular/core'; +import { eIdentityRouteNames } from '@abp/ng.identity'; @Injectable({ providedIn: 'root', @@ -8,29 +9,29 @@ export class IdentityConfigService { constructor() { addAbpRoutes([ { - name: 'AbpUiNavigation::Menu:Administration', + name: eIdentityRouteNames.Administration, path: '', order: 1, wrapper: true, iconClass: 'fa fa-wrench', }, { - name: 'AbpIdentity::Menu:IdentityManagement', + name: eIdentityRouteNames.IdentityManagement, path: 'identity', order: 1, - parentName: 'AbpUiNavigation::Menu:Administration', + parentName: eIdentityRouteNames.Administration, layout: eLayoutType.application, iconClass: 'fa fa-id-card-o', children: [ { path: 'roles', - name: 'AbpIdentity::Roles', + name: eIdentityRouteNames.Roles, order: 1, requiredPolicy: 'AbpIdentity.Roles', }, { path: 'users', - name: 'AbpIdentity::Users', + name: eIdentityRouteNames.Users, order: 2, requiredPolicy: 'AbpIdentity.Users', }, diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 752faded23..c6adde27e7 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.identity", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.identity.config": "^2.6.0", - "@abp/ng.permission-management": "^2.6.0", - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.identity.config": "^2.6.2", + "@abp/ng.permission-management": "^2.6.2", + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity/src/lib/enums/index.ts b/npm/ng-packs/packages/identity/src/lib/enums/index.ts new file mode 100644 index 0000000000..5ac9b6e8fa --- /dev/null +++ b/npm/ng-packs/packages/identity/src/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './route-names'; diff --git a/npm/ng-packs/packages/identity/src/lib/enums/route-names.ts b/npm/ng-packs/packages/identity/src/lib/enums/route-names.ts new file mode 100644 index 0000000000..15933a5b77 --- /dev/null +++ b/npm/ng-packs/packages/identity/src/lib/enums/route-names.ts @@ -0,0 +1,6 @@ +export const enum eIdentityRouteNames { + Administration = 'AbpUiNavigation::Menu:Administration', + IdentityManagement = 'AbpIdentity::Menu:IdentityManagement', + Roles = 'AbpIdentity::Roles', + Users = 'AbpIdentity::Users', +} diff --git a/npm/ng-packs/packages/identity/src/public-api.ts b/npm/ng-packs/packages/identity/src/public-api.ts index 1a2b217931..fd26c8a649 100644 --- a/npm/ng-packs/packages/identity/src/public-api.ts +++ b/npm/ng-packs/packages/identity/src/public-api.ts @@ -4,7 +4,7 @@ export * from './lib/identity.module'; export * from './lib/actions/identity.actions'; -export * from './lib/enums/components'; +export * from './lib/enums'; export * from './lib/components'; export * from './lib/models/identity'; export * from './lib/services'; diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index 41238c6d0b..5ebc5a3c46 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.permission-management", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/setting-management-config/package.json b/npm/ng-packs/packages/setting-management-config/package.json index 2e25ce47d5..8f425c1f4f 100644 --- a/npm/ng-packs/packages/setting-management-config/package.json +++ b/npm/ng-packs/packages/setting-management-config/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.setting-management.config", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", diff --git a/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts b/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts index 721a5a5991..a5b319ccb1 100644 --- a/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts +++ b/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts @@ -2,6 +2,7 @@ import { Injectable, Injector } from '@angular/core'; import { addAbpRoutes, eLayoutType, PatchRouteByName, ABP } from '@abp/ng.core'; import { getSettingTabs } from '@abp/ng.theme.shared'; import { Store } from '@ngxs/store'; +import { eSettingManagementRouteNames } from '@abp/ng.setting-management'; @Injectable({ providedIn: 'root', @@ -13,7 +14,7 @@ export class SettingManagementConfigService { constructor(private injector: Injector) { const route = { - name: 'AbpSettingManagement::Settings', + name: eSettingManagementRouteNames.Settings, path: 'setting-management', parentName: 'AbpUiNavigation::Menu:Administration', requiredPolicy: 'AbpAccount.SettingManagement', diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 26bfcfeb65..1203f3e253 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.setting-management", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.setting-management.config": "^2.6.0", - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.setting-management.config": "^2.6.2", + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/setting-management/src/lib/enums/index.ts b/npm/ng-packs/packages/setting-management/src/lib/enums/index.ts new file mode 100644 index 0000000000..5ac9b6e8fa --- /dev/null +++ b/npm/ng-packs/packages/setting-management/src/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './route-names'; diff --git a/npm/ng-packs/packages/setting-management/src/lib/enums/route-names.ts b/npm/ng-packs/packages/setting-management/src/lib/enums/route-names.ts new file mode 100644 index 0000000000..86ad145f5f --- /dev/null +++ b/npm/ng-packs/packages/setting-management/src/lib/enums/route-names.ts @@ -0,0 +1,3 @@ +export const enum eSettingManagementRouteNames { + Settings = 'AbpSettingManagement::Settings', +} diff --git a/npm/ng-packs/packages/setting-management/src/public-api.ts b/npm/ng-packs/packages/setting-management/src/public-api.ts index ac030c3ccf..24e62d4da1 100644 --- a/npm/ng-packs/packages/setting-management/src/public-api.ts +++ b/npm/ng-packs/packages/setting-management/src/public-api.ts @@ -1,3 +1,3 @@ export * from './lib/setting-management.module'; export * from './lib/components/setting-management.component'; -export * from './lib/enums/components'; +export * from './lib/enums'; diff --git a/npm/ng-packs/packages/tenant-management-config/package.json b/npm/ng-packs/packages/tenant-management-config/package.json index bc23d2f7d8..24dbe66160 100644 --- a/npm/ng-packs/packages/tenant-management-config/package.json +++ b/npm/ng-packs/packages/tenant-management-config/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.tenant-management.config", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", diff --git a/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts b/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts index 48a6d31c84..ab5ca4a081 100644 --- a/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts +++ b/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts @@ -1,25 +1,34 @@ import { Injectable } from '@angular/core'; import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; - +import { eTenantManagementRouteNames } from '@abp/ng.tenant-management'; @Injectable({ providedIn: 'root', }) export class TenantManagementConfigService { constructor() { - addAbpRoutes({ - name: 'AbpTenantManagement::Menu:TenantManagement', - path: 'tenant-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: eLayoutType.application, - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - requiredPolicy: 'AbpTenantManagement.Tenants', - }, - ], - }); + addAbpRoutes([ + { + name: eTenantManagementRouteNames.Administration, + path: '', + order: 1, + wrapper: true, + iconClass: 'fa fa-wrench', + }, + { + name: eTenantManagementRouteNames.TenantManagement, + path: 'tenant-management', + parentName: eTenantManagementRouteNames.Administration, + layout: eLayoutType.application, + iconClass: 'fa fa-users', + children: [ + { + path: 'tenants', + name: eTenantManagementRouteNames.Tenants, + order: 1, + requiredPolicy: 'AbpTenantManagement.Tenants', + }, + ], + }, + ]); } } diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 61a560140e..4ed62af205 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.tenant-management", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.feature-management": "^2.6.0", - "@abp/ng.tenant-management.config": "^2.6.0", - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.feature-management": "^2.6.2", + "@abp/ng.tenant-management.config": "^2.6.2", + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts b/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts new file mode 100644 index 0000000000..5ac9b6e8fa --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +export * from './route-names'; diff --git a/npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts b/npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts new file mode 100644 index 0000000000..8125e28a4f --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts @@ -0,0 +1,5 @@ +export const enum eTenantManagementRouteNames { + Administration = 'AbpUiNavigation::Menu:Administration', + TenantManagement = 'AbpTenantManagement::Menu:TenantManagement', + Tenants = 'AbpTenantManagement::Tenants', +} diff --git a/npm/ng-packs/packages/tenant-management/src/public-api.ts b/npm/ng-packs/packages/tenant-management/src/public-api.ts index 003074b2c4..3fc7cf90d5 100644 --- a/npm/ng-packs/packages/tenant-management/src/public-api.ts +++ b/npm/ng-packs/packages/tenant-management/src/public-api.ts @@ -1,7 +1,7 @@ export * from './lib/tenant-management.module'; export * from './lib/actions'; export * from './lib/components'; -export * from './lib/enums/components'; +export * from './lib/enums'; export * from './lib/models'; export * from './lib/services'; export * from './lib/states'; diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index bd01798972..990f60c150 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.theme.basic", - "version": "2.6.0", + "version": "2.6.2", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.6.0" + "@abp/ng.theme.shared": "^2.6.2" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index 2687c2cf81..438c315c16 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -5,7 +5,13 @@ >
- +
-

- {{ data.title | abpLocalization: data.options?.titleLocalizationParams }} -

-

- {{ data.message | abpLocalization: data.options?.messageLocalizationParams }} -

+

+

diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index f1ae22f3ae..af2db977fe 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -90,7 +90,7 @@ export class ErrorHandler { .subscribe(err => { const body = snq(() => err.error.error, DEFAULT_ERROR_MESSAGES.defaultError.title); - if (err.headers.get('_AbpErrorFormat')) { + if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { const confirmation$ = this.showError(null, null, body); if (err.status === 401) { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts index e43ff885f9..e70b88061f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts @@ -43,6 +43,25 @@ describe('ConfirmationService', () => { expect(selectConfirmationContent('.message')).toBe('MESSAGE'); })); + test('should display HTML string in title, message, and buttons', fakeAsync(() => { + service.show( + 'MESSAGE', + 'TITLE', + 'neutral', + { + cancelText: 'CANCEL', + yesText: 'YES', + }, + ); + + tick(); + + expect(selectConfirmationContent('.custom-title')).toBe('TITLE'); + expect(selectConfirmationContent('.custom-message')).toBe('MESSAGE'); + expect(selectConfirmationContent('.custom-cancel')).toBe('CANCEL'); + expect(selectConfirmationContent('.custom-yes')).toBe('YES'); + })); + test.each` type | selector | icon ${'info'} | ${'.info'} | ${'.fa-info-circle'} diff --git a/npm/ng-packs/packages/theme-shared/src/public-api.ts b/npm/ng-packs/packages/theme-shared/src/public-api.ts index 436f687044..9df78be2d9 100644 --- a/npm/ng-packs/packages/theme-shared/src/public-api.ts +++ b/npm/ng-packs/packages/theme-shared/src/public-api.ts @@ -6,6 +6,7 @@ export * from './lib/theme-shared.module'; export * from './lib/animations'; export * from './lib/components'; export * from './lib/directives'; +export * from './lib/handlers'; export * from './lib/models'; export * from './lib/services'; export * from './lib/tokens'; diff --git a/npm/ng-packs/scripts/build.ts b/npm/ng-packs/scripts/build.ts index 049b044033..63b2cb593d 100644 --- a/npm/ng-packs/scripts/build.ts +++ b/npm/ng-packs/scripts/build.ts @@ -32,8 +32,9 @@ import program from 'commander'; 'copy', '--angular', '--no-watch', - '--packages', - '@abp/ng.feature-management,@abp/ng.permission-management,@abp/ng.account.config,@abp/ng.identity.config,@abp/ng.setting-management.config,@abp/ng.tenant-management.config', + '--all-packages', + '--excluded-packages', + '@abp/ng.core,@abp/ng.theme.shared,@abp/ng.feature-management,@abp/ng.permission-management,@abp/ng.account.config,@abp/ng.identity.config,@abp/ng.setting-management.config,@abp/ng.tenant-management.config', ], { stdout: 'inherit', cwd: '../' }, ); @@ -45,9 +46,8 @@ import program from 'commander'; 'copy', '--angular', '--no-watch', - '--all-packages', - '--excluded-packages', - '@abp/ng.core,@abp/ng.theme.shared,@abp/ng.feature-management,@abp/ng.permission-management,@abp/ng.account.config,@abp/ng.identity.config,@abp/ng.setting-management.config,@abp/ng.tenant-management.config', + '--packages', + '@abp/ng.feature-management,@abp/ng.permission-management,@abp/ng.account.config,@abp/ng.identity.config,@abp/ng.setting-management.config,@abp/ng.tenant-management.config', ], { stdout: 'inherit', cwd: '../' }, ); diff --git a/npm/ng-packs/scripts/install-new-dependencies.ts b/npm/ng-packs/scripts/install-new-dependencies.ts index d26d72f88b..3528396fb9 100644 --- a/npm/ng-packs/scripts/install-new-dependencies.ts +++ b/npm/ng-packs/scripts/install-new-dependencies.ts @@ -17,7 +17,7 @@ const updateAndInstall = async () => { ...packageJson.devDependencies, ...dependencies, ...peerDependencies, - [name]: `^${version}`, + [name]: `~${version}`, }; packageJson.devDependencies = Object.keys(packageJson.devDependencies) diff --git a/npm/ng-packs/scripts/prod-build.ts b/npm/ng-packs/scripts/prod-build.ts index a6bad4c8c9..03a1432b99 100644 --- a/npm/ng-packs/scripts/prod-build.ts +++ b/npm/ng-packs/scripts/prod-build.ts @@ -2,6 +2,11 @@ import execa from 'execa'; import fse from 'fs-extra'; (async () => { + await execa('yarn', ['ng', 'build', '--prod'], { + stdout: 'inherit', + cwd: '..', + }); + await execa('yarn', ['install', '--ignore-scripts'], { stdout: 'inherit', cwd: '../../../templates/app/angular', diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index 762e718e3f..e8539d81be 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -2,28 +2,28 @@ # yarn lockfile v1 -"@abp/ng.account.config@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.account.config/-/ng.account.config-2.6.0.tgz#4e18767516ee130f421d58f3162404f720441111" - integrity sha512-FZcohv/5t+r1d5Q1H7Z1Yv92sK+jJFOxQWFXOWI6hiLPOV/tMQxMHgPV+Gr+D9lZRqiNqMU97oYnx6VHyFPUaQ== +"@abp/ng.account.config@^2.6.2", "@abp/ng.account.config@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.account.config/-/ng.account.config-2.6.2.tgz#1a3e935b7144525c62f5d08adcd06d2ee12f7c9e" + integrity sha512-dC0/H1dkeQqYbu/dNISQA7NLpdOughhZSxT/7zjq6cBP8gYDceW0wYT5Rh0qIAkYWh2jO4BMXIFqzlH31Jg/XA== dependencies: tslib "^1.9.0" -"@abp/ng.account@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.account/-/ng.account-2.6.0.tgz#91ac2d60fdc68058968d53c917bc518ba8f7a061" - integrity sha512-1qEx03lTJeJu7aXoRQhRoOpIzTDgBy4oJB+/jbw8Zj8ULfuqXyOtpe//s0r79j0Ld+l3Jt1EobM94WgfLOcsEw== +"@abp/ng.account@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.account/-/ng.account-2.6.2.tgz#aac921c49bcd8cdec506bd27d2546193416ea187" + integrity sha512-s3gLSqNdCW6D+DvuML+aGFT8braCfSXUbxvJ1WRCovd2Ec0jNxPBKj84zgYPYEb3smD294DcXxNx36jtJK+1OA== dependencies: - "@abp/ng.account.config" "^2.6.0" - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.account.config" "^2.6.2" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-2.6.0.tgz#0c299bedad887cf407f568448965b7141bf9114a" - integrity sha512-1bpsz4t2r7tFW+fpM1pC8Vt5aurFDI1F+GWnf4JdV6Vteawbe+2CJ5yTvgCFdP2hJ4zC/4ZMI/44+j7R/9claA== +"@abp/ng.core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-2.6.2.tgz#d0e4f6666a9a207fc7425001449bea2cdfe93522" + integrity sha512-e7c82d9VF20d4JFUkyzT5iTqGKnkq5FBHdv6IW6lsVyuz2Zf/p4wNtKATRr2ntM7qNIjIQebtGNFyxxX4nmL+w== dependencies: - "@abp/utils" "^2.4.0" + "@abp/utils" "^2.6.0" "@angular/localize" "~9.1.0" "@ngxs/router-plugin" "^3.6.2" "@ngxs/storage-plugin" "^3.6.2" @@ -35,86 +35,86 @@ ts-toolbelt "^6.3.6" tslib "^1.9.0" -"@abp/ng.feature-management@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-2.6.0.tgz#54f114794bc0958ba3a5ca384f64c2e5aa768b33" - integrity sha512-SPgP9VTw3PMgai84YmBtWwOzXr4lG15qNdVNs63zM3TAeV/o+fewA4WSS9HcC6nJCpoI8bP9iKD0nJsIzFLQSg== +"@abp/ng.feature-management@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-2.6.2.tgz#da09b07ebe81f09ccf338a7c10c384920375567a" + integrity sha512-JdHuAZk9j9AR1iKU02Xoux40ULOccm+68q7/4L2G1cBET6yTAE8Ua9RPPX9HadmCG0cR8awl3d+5JPwyL3OjFA== dependencies: - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.identity.config@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.identity.config/-/ng.identity.config-2.6.0.tgz#c87c36ef169e6d40b633e17e8b7bac39adbc8569" - integrity sha512-vqq9C5M1AsHXKEkq3Y1oXgWTeQ4qL1DEx7JWlhPLWv2vTOj1q+m3SuJblimgAY9o8rGBTlCVD0to4XhbaSYdxQ== +"@abp/ng.identity.config@^2.6.2", "@abp/ng.identity.config@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.identity.config/-/ng.identity.config-2.6.2.tgz#79384ae4b11e53e8bf9dda25d3400def05c5beb8" + integrity sha512-jCqt2ZWEkRA9foJjHwRJtw5JNy03OVge9dUj0eAJ7Vo13IrKAT1kCNlD7cRM9xJ42Ga2utEqqz9FCJHgR2IxbQ== dependencies: tslib "^1.9.0" -"@abp/ng.identity@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-2.6.0.tgz#ce5106a112b0fa8af8424509dcebf3aab071f135" - integrity sha512-aiZOLROxU9jHYTv591humy9NSsVVt3MBJxbYH1O0n/6T2s4BrbT6hI+4lOmdcM0P2O32yyzMTKzu4B17ldlZFQ== +"@abp/ng.identity@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-2.6.2.tgz#14f83305415d27b67d01405e582978682cfd7f22" + integrity sha512-wzR/QFoSSRhIxfH91QsTSCc0rBsFfsg8aiP5xPheNx3/lZygCnMogH3blRgLiNIoyxuG00MTfp+e9lFIBfDnbQ== dependencies: - "@abp/ng.identity.config" "^2.6.0" - "@abp/ng.permission-management" "^2.6.0" - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.identity.config" "^2.6.2" + "@abp/ng.permission-management" "^2.6.2" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.permission-management@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-2.6.0.tgz#656e4b6dcc485607012356215b0731f11a207e0d" - integrity sha512-jKkepi0h/oJToEYgWDma4LziMle73QiuB3GfBz0CMLgtglpi6Ok+hnSj0PrafYmK5SYBL5k4zOkDSvxclb3Tsg== +"@abp/ng.permission-management@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-2.6.2.tgz#ba7902256acf3e1ff49552983e4d292f670f9ad8" + integrity sha512-6NW5BF+5ZFQI8oLEQWCiJS5iF2v3gHLvohrbN84RsPNTc+ZRDte6MbIOGq2CWxM8qXwisLBiR/RWC4ssgfPqEA== dependencies: - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.setting-management.config@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.setting-management.config/-/ng.setting-management.config-2.6.0.tgz#f348833a16507ac6443ebe4a4d7f13f4dd8bddad" - integrity sha512-H3GZFtUTXco7jqlj/i1R7Jf6GgyuEAVZoka0BF2WXmWTYr4eXvpIxbM3p1GdO5u6lXuFJJN33P37yp9pEEb61w== +"@abp/ng.setting-management.config@^2.6.2", "@abp/ng.setting-management.config@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.setting-management.config/-/ng.setting-management.config-2.6.2.tgz#43e9e4399d65efc1aee6e314469c303ce22145e9" + integrity sha512-3F0vq5nvBTat2q7S0iBfdEsyPuZHJuL3+9z4Voq4Q9rA2T2VIAUMp9aBsfI2s2ycwGbsb7kCOWTKo/2by1b4AA== dependencies: tslib "^1.9.0" -"@abp/ng.setting-management@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-2.6.0.tgz#5562f32a0ce94d38bb48a5036243c45ece359659" - integrity sha512-PitIsMCVL3lEc7jbhJoVtvgxr1+dNO6MZb48jwC7YxVkH5c6CRkSkRjQpggfFpLmazccOtTch4BjtGLyALlAEQ== +"@abp/ng.setting-management@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-2.6.2.tgz#f49d361ed8412380fdf2173f28b890115748a52b" + integrity sha512-0VdGa+t8lEZRsUdKL1EHoZei+ZCDMTu4n3UvXvD0OzRJyYPUjHRrMSSOYifuN4gPatluIe0E2I+tO3lQ3/2tjw== dependencies: - "@abp/ng.setting-management.config" "^2.6.0" - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.setting-management.config" "^2.6.2" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.tenant-management.config@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management.config/-/ng.tenant-management.config-2.6.0.tgz#086c040e62603cf1874a65125e4a152d16ed988b" - integrity sha512-IGYBNE6MkykWorV433uDkEqq1E1nTLZ5ty9u77PHTAZ6f+ijWvN/IS+UDmgmgFP6FJkkgCJeZ8sbMFa0o+Lihg== +"@abp/ng.tenant-management.config@^2.6.2", "@abp/ng.tenant-management.config@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management.config/-/ng.tenant-management.config-2.6.2.tgz#9a9365ad59bdd2053d7bd33fb623ce633de40b24" + integrity sha512-iZ49Els+J9lbRKTSeNU/KIJuJS7Le3dAk1pdqLg/g5iw7e6uTEih1qeDvJdcXpSKfAZJrOd/KKDjFHIzVrveuA== dependencies: tslib "^1.9.0" -"@abp/ng.tenant-management@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-2.6.0.tgz#792b1376ebf53d5ebafcbdc601e86beffc2a559f" - integrity sha512-AdKYodZhUS2gq5kWijGOUzpfn2YzhKiIqAyvMC5/CRQ3X1ijbq1D0YfMN/CLhWkVQzEs/PjXUcFceKXbhCmsgw== +"@abp/ng.tenant-management@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-2.6.2.tgz#6be284fe6691946e2e6766e34ae958d7d2ec5e3a" + integrity sha512-BGARok01Eqn1kEltOwjwIpMJ8LD0jtYfbSbnO8V9TRg7f4ZEqFmfXudOIluDIwHjSEkNXk+Cs0/iV53lyi5+lg== dependencies: - "@abp/ng.feature-management" "^2.6.0" - "@abp/ng.tenant-management.config" "^2.6.0" - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.feature-management" "^2.6.2" + "@abp/ng.tenant-management.config" "^2.6.2" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-2.6.0.tgz#536fb2be25a18f0f38b34d43afba008174d0cde5" - integrity sha512-KfbsFUEwsOrUi9Eo0sLrDfYwfGoDp7t+eZi7bssEN8XT0aH86yayDoKsgWOcv8nRaKT7p3Ou+zuq0zOGMc2ICw== +"@abp/ng.theme.basic@~2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-2.6.2.tgz#4b0414411e164885b8c467b04d635a0a3c81bd01" + integrity sha512-qgA4hh7ipIWY88JOqjChjoJgFB0csqXbwdb/pabrsFoHYuiFVT3cqvHq6jMfKQHEPBJSEnl6/GUIm+26DQ5eDw== dependencies: - "@abp/ng.theme.shared" "^2.6.0" + "@abp/ng.theme.shared" "^2.6.2" tslib "^1.9.0" -"@abp/ng.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-2.6.0.tgz#919174e396c3fda4b261826ed39eb92c8eff72b4" - integrity sha512-5PtsRi5hsMgjkoi6HYpIiLYlqyM5B+m1XvLYFUkLIl5GYIwsmhrxZEUKhc88KkTfMd5jMfjYOjfE32dhyEPStw== +"@abp/ng.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-2.6.2.tgz#9d70b7aab010af4cd5a451c9c2d162d73ec5f9cc" + integrity sha512-e3QVsPpmbPGmTpQY9J1RgmWer7Tx7mrsOtxjFvbJGqKVF1UUtU5Kc3VyGPb3JsjQtT+890/9qmAf/YSj3cNPIA== dependencies: - "@abp/ng.core" "^2.6.0" + "@abp/ng.core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.12.1" "@ng-bootstrap/ng-bootstrap" "^5.3.0" "@ngx-validate/core" "^0.0.7" @@ -122,10 +122,10 @@ chart.js "^2.9.3" tslib "^1.9.0" -"@abp/utils@^2.4.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.5.0.tgz#18b246367a9485bb8fa3095901477c82f3d54ad5" - integrity sha512-rAR8SGAT4lZb1CVrY0Q42QRgce+YxtWW0fU0GV+pERZVeKFaGRe6OkVnDy03DYy54UlWvZTFGPFnb6hEHzYOsQ== +"@abp/utils@^2.6.0": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" @@ -356,9 +356,9 @@ integrity sha512-7EhN9JJbAJcH2xCa+rIOmekjiEuB0qwPdHuD5qn/wwMfRzMZo+Db4hHbR9KHrLH6H82PTwYKye/LLpDaZqoHOA== "@angular/localize@~9.1.0": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.1.tgz#267d7cecb288b3019482744330da0ef9e049c85d" - integrity sha512-5mYdSL7IwqFXWRgBXj9c5vHT15AQy2kjD40fJJsmUx4WUFr+uf0Ss9ADCfL2FjCHpATrjYKpiyJs1mAF60USPQ== + version "9.1.3" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.3.tgz#d03d9cb1b4fe10dabaf806062d23ee1b7774945d" + integrity sha512-aV7AFgvEWm6aaEjS45EPJ1U4i2DOVR6DswUu9CsYkRfy/gSOxWE95SyrhLdg2hB64Bu4mcXxiV1So2BUI7NzBA== dependencies: "@babel/core" "7.8.3" glob "7.1.2" @@ -2101,9 +2101,9 @@ glob-to-regexp "^0.3.0" "@ng-bootstrap/ng-bootstrap@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-5.3.0.tgz#4d624dd70a93f99c38bc35b1d3e00afc490155fc" - integrity sha512-oNg1sgT+XXxwSBX9oF4AeCHrq+Nm1CvukfVT/NPuYiuNAoTeVOdXc5bJHTLd+cCwoZ1OPZN/yZT+1BcW1KCksQ== + version "5.3.1" + resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-5.3.1.tgz#4cf2929583e9572475d1d0f3c905b05885a086bb" + integrity sha512-xlhBJb4oNcOQk04h5sQcq9P1E97sGB1HjqBCqgL0+S2w2uvLWME9F9SuH7wU4S1+eYe7WG9SKFpq+R4BjG2kMw== dependencies: tslib "^1.9.0" @@ -2183,12 +2183,12 @@ dependencies: "@octokit/types" "^2.0.0" -"@octokit/endpoint@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.0.tgz#4c7acd79ab72df78732a7d63b09be53ec5a2230b" - integrity sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A== +"@octokit/endpoint@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.1.tgz#16d5c0e7a83e3a644d1ddbe8cded6c3d038d31d7" + integrity sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A== dependencies: - "@octokit/types" "^2.0.0" + "@octokit/types" "^2.11.1" is-plain-object "^3.0.0" universal-user-agent "^5.0.0" @@ -2236,13 +2236,13 @@ once "^1.4.0" "@octokit/request@^5.2.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.0.tgz#52382c830f0cf3295b5a03e308872ac0f5ccba9b" - integrity sha512-uAJO6GI8z8VHBqtY7VTL9iFy1Y+UTp5ShpI97tY5z0qBfYKE9rZCRsCm23VmF00x+IoNJ7a0nuVITs/+wS9/mg== + version "5.4.2" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.2.tgz#74f8e5bbd39dc738a1b127629791f8ad1b3193ee" + integrity sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw== dependencies: - "@octokit/endpoint" "^6.0.0" + "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^2.8.2" + "@octokit/types" "^2.11.1" deprecation "^2.0.0" is-plain-object "^3.0.0" node-fetch "^2.3.0" @@ -2271,10 +2271,10 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@octokit/types@^2.0.0", "@octokit/types@^2.0.1", "@octokit/types@^2.8.2": - version "2.9.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.9.0.tgz#b3b69cddab629dd31a559ffb25c9f471301d7538" - integrity sha512-IzptUpoDsFlXF+AOys+KnfItIVY3EK+eH9Akv+lJYELnMSGkJnIcClt6Cm0QRR4ecsUTsmFJWn10iFgJ9BQqIQ== +"@octokit/types@^2.0.0", "@octokit/types@^2.0.1", "@octokit/types@^2.11.1": + version "2.12.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.12.1.tgz#4a26b4a85ec121043d3b0745b5798f9d8fd968ca" + integrity sha512-LRLR1tjbcCfAmUElvTmMvLEzstpx6Xt/aQVTg2xvd+kHA2Ekp1eWl5t+gU7bcwjXHYEAzh4hH4WH+kS3vh+wRw== dependencies: "@types/node" ">= 8" @@ -2429,9 +2429,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*", "@types/node@>= 8": - version "13.11.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7" - integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g== + version "13.13.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.4.tgz#1581d6c16e3d4803eb079c87d4ac893ee7501c2c" + integrity sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA== "@types/node@^8.0.31": version "8.10.60" @@ -2789,9 +2789,9 @@ ajv@6.9.1: uri-js "^4.2.2" ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -3154,9 +3154,9 @@ babel-jest@^24.9.0: slash "^2.0.0" babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" @@ -3437,12 +3437,12 @@ browserslist@4.10.0: pkg-up "^3.1.0" browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.6.3, browserslist@^4.8.5, browserslist@^4.9.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.1.tgz#92f855ee88d6e050e7e7311d987992014f1a1f1b" - integrity sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g== + version "4.12.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" + integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== dependencies: - caniuse-lite "^1.0.30001038" - electron-to-chromium "^1.3.390" + caniuse-lite "^1.0.30001043" + electron-to-chromium "^1.3.413" node-releases "^1.1.53" pkg-up "^2.0.0" @@ -3677,10 +3677,10 @@ caniuse-lite@1.0.30001035: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== -caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001038, caniuse-lite@^1.0.30001039: - version "1.0.30001042" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz#c91ec21ec2d270bd76dbc2ce261260c292b8c93c" - integrity sha512-igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw== +caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001039, caniuse-lite@^1.0.30001043: + version "1.0.30001048" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001048.tgz#4bb4f1bc2eb304e5e1154da80b93dee3f1cf447e" + integrity sha512-g1iSHKVxornw0K8LG9LLdf+Fxnv7T1Z+mMsf0/YYLclQX4Cd522Ap0Lrw6NFqHgezit78dtyWxzlV2Xfc7vgRg== canonical-path@1.0.0: version "1.0.0" @@ -3768,9 +3768,9 @@ chokidar@2.0.4: fsevents "^1.2.2" "chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.0.2: - version "3.3.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" - integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + version "3.4.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" + integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -3778,7 +3778,7 @@ chokidar@2.0.4: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.3.0" + readdirp "~3.4.0" optionalDependencies: fsevents "~2.1.2" @@ -5023,10 +5023,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.390: - version "1.3.407" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.407.tgz#361dbfc92a3d1b0b097b77221256271967e04c0e" - integrity sha512-ayDQgkwCLHq0AVmsU10t2H/8X9lq3nb1CRV9rWYsc2nTTbFMvh4Aaau48Ao0iW/L+Q8Y9bgbJ5iHyPse7DGMJw== +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.413: + version "1.3.418" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.418.tgz#840021191f466b803a873e154113620c9f53cec6" + integrity sha512-i2QrQtHes5fK/F9QGG5XacM5WKEuR322fxTYF9e8O9Gu0mc0WmjjwGpV8c7Htso6Zf2Di18lc3SIPxmMeRFBug== elliptic@^6.0.0: version "6.5.2" @@ -5104,9 +5104,9 @@ env-paths@^2.2.0: integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== envinfo@^7.3.1: - version "7.5.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.0.tgz#91410bb6db262fb4f1409bd506e9ff57e91023f4" - integrity sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ== + version "7.5.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236" + integrity sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ== err-code@^1.0.0: version "1.1.2" @@ -5503,9 +5503,9 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figlet@^1.2.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.3.0.tgz#c49e3d92907ba13bebadc7124f76ba71f1f32ef0" - integrity sha512-f7A8aOJAfyehLJ7lQ6rEA8WJw7kOk3lfWRi5piSjkzbK5YkI5sqO8eiLHz1ehO+DM0QYB85i8VfA6XIGUbU1dg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.4.0.tgz#21c5878b3752a932ebdb8be400e2d10bbcddfd60" + integrity sha512-CxxIjEKHlqGosgXaIA+sikGDdV6KZOOlzPJnYuPgQlOSHZP5h9WIghYI30fyXnwEVeSH7Hedy72gC6zJrFC+SQ== figures@^2.0.0: version "2.0.0" @@ -5744,9 +5744,9 @@ fsevents@^1.2.2, fsevents@^1.2.7: nan "^2.12.1" fsevents@~2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== function-bind@^1.1.1: version "1.1.1" @@ -6861,11 +6861,6 @@ is-plain-object@^3.0.0: dependencies: isobject "^4.0.0" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - is-reference@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427" @@ -7612,9 +7607,9 @@ jsrsasign@^8.0.12: integrity sha512-6UKHqnNs5lYROn03wf1BTw7DQx5tW616DTigjbo0JHV97D3HzIqYmPVCBSNsfEfQOrfpFqmPZJvaC3cMNOT0Yw== jszip@^3.1.3: - version "3.3.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271" - integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw== + version "3.4.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350" + integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -8052,9 +8047,9 @@ make-dir@^2.0.0, make-dir@^2.1.0: semver "^5.6.0" make-dir@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" - integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" @@ -8265,17 +8260,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.43.0" + mime-db "1.44.0" mime@1.6.0, mime@^1.4.1: version "1.6.0" @@ -8347,11 +8342,6 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= - minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -8490,9 +8480,9 @@ mz@^2.5.0: thenify-all "^1.0.0" nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== nanomatch@^1.2.9: version "1.2.13" @@ -8559,7 +8549,7 @@ ng-packagr@^5.7.1: terser "^4.1.2" update-notifier "^3.0.0" -ngxs-reset-plugin@^1.2.0: +ngxs-reset-plugin@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ngxs-reset-plugin/-/ngxs-reset-plugin-1.3.0.tgz#370b89190e729de94f0116b99ff65389582a7626" integrity sha512-gxAUuka1ewSs04M+cFaUu5NVMSWW9EHw0mZAxXbtagtbPyEwSRjgFvobb4nNyWVVBPsdAuBIfPCfkztZ6Dtj3w== @@ -9006,14 +8996,6 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -optimist@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -9451,7 +9433,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.7: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -9709,9 +9691,9 @@ protoduck@^5.0.1: genfun "^5.0.0" protractor@~5.4.0: - version "5.4.3" - resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.4.3.tgz#35f050741e404a45868618ea648745d89af31683" - integrity sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA== + version "5.4.4" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.4.4.tgz#b241466aaf83b76bc2c58df67deb9a5cdfc61529" + integrity sha512-BaL4vePgu3Vfa/whvTUAlgaCAId4uNSGxIFSCXMgj7LMYENPWLp85h5RBi9pdpX/bWQ8SF6flP7afmi2TC4eHw== dependencies: "@types/q" "^0.0.32" "@types/selenium-webdriver" "^3.0.0" @@ -9721,13 +9703,13 @@ protractor@~5.4.0: glob "^7.0.3" jasmine "2.8.0" jasminewd2 "^2.1.0" - optimist "~0.6.0" q "1.4.1" saucelabs "^1.5.0" selenium-webdriver "3.6.0" source-map-support "~0.4.0" webdriver-js-extender "2.1.0" webdriver-manager "^12.0.6" + yargs "^12.0.5" proxy-addr@~2.0.5: version "2.0.6" @@ -10048,12 +10030,12 @@ readdirp@^2.0.0, readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" - integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== dependencies: - picomatch "^2.0.7" + picomatch "^2.2.1" realpath-native@^1.1.0: version "1.1.0" @@ -10320,9 +10302,9 @@ resolve@1.1.7, resolve@~1.1.6: integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" @@ -10457,11 +10439,9 @@ rsvp@^4.8.4: integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== run-async@^2.2.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== - dependencies: - is-promise "^2.1.0" + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" @@ -10547,9 +10527,9 @@ sass@1.22.9: chokidar ">=2.0.0 <4.0.0" sass@^1.17.3: - version "1.26.3" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.3.tgz#412df54486143b76b5a65cdf7569e86f44659f46" - integrity sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw== + version "1.26.5" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.5.tgz#2d7aecfbbabfa298567c8f06615b6e24d2d68099" + integrity sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -10587,9 +10567,9 @@ schema-utils@^1.0.0: ajv-keywords "^3.1.0" schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.6.1: - version "2.6.5" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" - integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== + version "2.6.6" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.6.tgz#299fe6bd4a3365dc23d99fd446caff8f1d6c330c" + integrity sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA== dependencies: ajv "^6.12.0" ajv-keywords "^3.4.1" @@ -10952,9 +10932,9 @@ source-map-support@0.5.13: source-map "^0.6.0" source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -11014,9 +10994,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.0" @@ -11529,9 +11509,9 @@ terser@4.6.3: source-map-support "~0.5.12" terser@^4.1.2: - version "4.6.11" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f" - integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA== + version "4.6.12" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.12.tgz#44b98aef8703fdb09a3491bf79b43faffc5b4fee" + integrity sha512-fnIwuaKjFPANG6MAixC/k1TDtnl1YlPLUlLVIxxGZUn1gfUx2+l3/zGNB72wya+lgsb50QBi2tUV75RiODwnww== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -11740,9 +11720,9 @@ ts-node@~7.0.0: yn "^2.0.0" ts-toolbelt@^6.3.6: - version "6.4.2" - resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.4.2.tgz#97df4fc577eccccfbe8929d82ab364d86ea99548" - integrity sha512-B27axztBC+3K4D1cuaRO3ygMcyR54LRpQxlt1zNIS9RnqDwrlDoVlIIazDqWxLuRaFzGWNvDLkjDgs3eeLEVig== + version "6.5.1" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.5.1.tgz#1316b8c6522797c0bee289ca5deaa159af846231" + integrity sha512-zjnZ/vy1eUA0li3H0JXecl0R3jiP42snpLimsrppt9V3LLbM4NM4jMgjXQ4S67hvehq+r9CxpX4Wj6RnFRzReA== tsickle@^0.37.0: version "0.37.1" @@ -11848,9 +11828,9 @@ typescript@^3.3.3333, typescript@^3.5.2: integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== uglify-js@^3.1.4: - version "3.9.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.0.tgz#037163a936992050ed5d14f5d5c4014126019661" - integrity sha512-j5wNQBWaql8gr06dOUrfaohHlscboQZ9B8sNsoK5o4sBjm7Ht9dxSbrMXyktQpA16Acaij8AcoozteaPYZON0g== + version "3.9.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.1.tgz#a56a71c8caa2d36b5556cc1fd57df01ae3491539" + integrity sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA== dependencies: commander "~2.20.3" @@ -12399,11 +12379,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -12600,14 +12575,14 @@ yargs-parser@^15.0.1: decamelize "^1.2.0" yargs-parser@^18.1.0, yargs-parser@^18.1.1: - version "18.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" - integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@12.0.5: +yargs@12.0.5, yargs@^12.0.5: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== diff --git a/npm/packs/anchor-js/package.json b/npm/packs/anchor-js/package.json index c538f3c16c..1d8343fe96 100644 --- a/npm/packs/anchor-js/package.json +++ b/npm/packs/anchor-js/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/anchor-js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "anchor-js": "^4.2.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json index 8c40ffc32e..f21129d90b 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json @@ -1,11 +1,11 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/aspnetcore.mvc.ui.theme.basic", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.shared": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json index ae52d8d137..e1ff6dd91d 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json @@ -1,24 +1,24 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/aspnetcore.mvc.ui.theme.shared", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui": "^2.6.0", - "@abp/bootstrap": "^2.6.0", - "@abp/bootstrap-datepicker": "^2.6.0", - "@abp/datatables.net-bs4": "^2.6.0", - "@abp/font-awesome": "^2.6.0", - "@abp/jquery-form": "^2.6.0", - "@abp/jquery-validation-unobtrusive": "^2.6.0", - "@abp/lodash": "^2.6.0", - "@abp/luxon": "^2.6.0", - "@abp/malihu-custom-scrollbar-plugin": "^2.6.0", - "@abp/select2": "^2.6.0", - "@abp/sweetalert": "^2.6.0", - "@abp/timeago": "^2.6.0", - "@abp/toastr": "^2.6.0" + "@abp/aspnetcore.mvc.ui": "^2.6.2", + "@abp/bootstrap": "^2.6.2", + "@abp/bootstrap-datepicker": "^2.6.2", + "@abp/datatables.net-bs4": "^2.6.2", + "@abp/font-awesome": "^2.6.2", + "@abp/jquery-form": "^2.6.2", + "@abp/jquery-validation-unobtrusive": "^2.6.2", + "@abp/lodash": "^2.6.2", + "@abp/luxon": "^2.6.2", + "@abp/malihu-custom-scrollbar-plugin": "^2.6.2", + "@abp/select2": "^2.6.2", + "@abp/sweetalert": "^2.6.2", + "@abp/timeago": "^2.6.2", + "@abp/toastr": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/aspnetcore.mvc.ui/package.json b/npm/packs/aspnetcore.mvc.ui/package.json index d4771d6d77..6c51275891 100644 --- a/npm/packs/aspnetcore.mvc.ui/package.json +++ b/npm/packs/aspnetcore.mvc.ui/package.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/aspnetcore.mvc.ui", "publishConfig": { "access": "public" @@ -12,5 +12,5 @@ "path": "^0.12.7", "rimraf": "^3.0.0" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/blogging/package.json b/npm/packs/blogging/package.json index 9f430704a2..dbb0c09673 100644 --- a/npm/packs/blogging/package.json +++ b/npm/packs/blogging/package.json @@ -1,13 +1,13 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/blogging", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "^2.6.0", - "@abp/owl.carousel": "^2.6.0", - "@abp/tui-editor": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.shared": "^2.6.2", + "@abp/owl.carousel": "^2.6.2", + "@abp/tui-editor": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/bootstrap-datepicker/package.json b/npm/packs/bootstrap-datepicker/package.json index 03fd16487f..574ebbfbc8 100644 --- a/npm/packs/bootstrap-datepicker/package.json +++ b/npm/packs/bootstrap-datepicker/package.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/bootstrap-datepicker", "publishConfig": { "access": "public" @@ -7,5 +7,5 @@ "dependencies": { "bootstrap-datepicker": "^1.9.0" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/bootstrap/package.json b/npm/packs/bootstrap/package.json index ff898045ab..11055fb8e9 100644 --- a/npm/packs/bootstrap/package.json +++ b/npm/packs/bootstrap/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/bootstrap", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "bootstrap": "^4.3.1" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/chart.js/package.json b/npm/packs/chart.js/package.json index c639f55a21..d34f62e831 100644 --- a/npm/packs/chart.js/package.json +++ b/npm/packs/chart.js/package.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/chart.js", "publishConfig": { "access": "public" @@ -7,5 +7,5 @@ "dependencies": { "chart.js": "^2.9.3" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/clipboard/package.json b/npm/packs/clipboard/package.json index 08a317b3b0..87bf7d4a46 100644 --- a/npm/packs/clipboard/package.json +++ b/npm/packs/clipboard/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/clipboard", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "clipboard": "^2.0.4" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/codemirror/package.json b/npm/packs/codemirror/package.json index 28cac90d41..9ec563a343 100644 --- a/npm/packs/codemirror/package.json +++ b/npm/packs/codemirror/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/codemirror", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "codemirror": "^5.49.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/core/package.json b/npm/packs/core/package.json index 0fc6236b87..1c841574cc 100644 --- a/npm/packs/core/package.json +++ b/npm/packs/core/package.json @@ -1,11 +1,11 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/core", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/utils": "^2.6.0" + "@abp/utils": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/datatables.net-bs4/package.json b/npm/packs/datatables.net-bs4/package.json index 4ba888aca7..8d9413d971 100644 --- a/npm/packs/datatables.net-bs4/package.json +++ b/npm/packs/datatables.net-bs4/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/datatables.net-bs4", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/datatables.net": "^2.6.0", + "@abp/datatables.net": "^2.6.2", "datatables.net-bs4": "^1.10.20" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/datatables.net/package.json b/npm/packs/datatables.net/package.json index 015f0198c3..16bbf82e95 100644 --- a/npm/packs/datatables.net/package.json +++ b/npm/packs/datatables.net/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/datatables.net", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "^2.6.0", + "@abp/jquery": "^2.6.2", "datatables.net": "^1.10.20" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/docs/package.json b/npm/packs/docs/package.json index 08cea9ab72..f4b764643a 100644 --- a/npm/packs/docs/package.json +++ b/npm/packs/docs/package.json @@ -1,15 +1,15 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/docs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/anchor-js": "^2.6.0", - "@abp/clipboard": "^2.6.0", - "@abp/malihu-custom-scrollbar-plugin": "^2.6.0", - "@abp/popper.js": "^2.6.0", - "@abp/prismjs": "^2.6.0" + "@abp/anchor-js": "^2.6.2", + "@abp/clipboard": "^2.6.2", + "@abp/malihu-custom-scrollbar-plugin": "^2.6.2", + "@abp/popper.js": "^2.6.2", + "@abp/prismjs": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/flag-icon-css/package.json b/npm/packs/flag-icon-css/package.json index 93e8351eb3..11373e9e89 100644 --- a/npm/packs/flag-icon-css/package.json +++ b/npm/packs/flag-icon-css/package.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/flag-icon-css", "publishConfig": { "access": "public" @@ -7,5 +7,5 @@ "dependencies": { "flag-icon-css": "^3.4.5" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/font-awesome/package.json b/npm/packs/font-awesome/package.json index f34bfe4487..8de8bb8db9 100644 --- a/npm/packs/font-awesome/package.json +++ b/npm/packs/font-awesome/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/font-awesome", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "@fortawesome/fontawesome-free": "^5.11.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/highlight.js/package.json b/npm/packs/highlight.js/package.json index 4751b2bd74..74c54833fb 100644 --- a/npm/packs/highlight.js/package.json +++ b/npm/packs/highlight.js/package.json @@ -1,11 +1,11 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/highlight.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0" + "@abp/core": "^2.6.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/jquery-form/package.json b/npm/packs/jquery-form/package.json index 9b7c4b663e..e400e2f7b1 100644 --- a/npm/packs/jquery-form/package.json +++ b/npm/packs/jquery-form/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/jquery-form", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "^2.6.0", + "@abp/jquery": "^2.6.2", "jquery-form": "^4.2.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/jquery-validation-unobtrusive/package.json b/npm/packs/jquery-validation-unobtrusive/package.json index ea8542d9cb..2e41c51bf5 100644 --- a/npm/packs/jquery-validation-unobtrusive/package.json +++ b/npm/packs/jquery-validation-unobtrusive/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/jquery-validation-unobtrusive", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery-validation": "^2.6.0", + "@abp/jquery-validation": "^2.6.2", "jquery-validation-unobtrusive": "^3.2.11" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/jquery-validation/package.json b/npm/packs/jquery-validation/package.json index 3db383aa5c..b55d7a2d82 100644 --- a/npm/packs/jquery-validation/package.json +++ b/npm/packs/jquery-validation/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/jquery-validation", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "^2.6.0", + "@abp/jquery": "^2.6.2", "jquery-validation": "^1.19.1" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/jquery/package.json b/npm/packs/jquery/package.json index afe5d83a31..7081201e65 100644 --- a/npm/packs/jquery/package.json +++ b/npm/packs/jquery/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/jquery", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "jquery": "^3.4.1" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/lodash/package.json b/npm/packs/lodash/package.json index 6a59d24e2f..378bb6719e 100644 --- a/npm/packs/lodash/package.json +++ b/npm/packs/lodash/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/lodash", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "lodash": "^4.17.15" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/luxon/package.json b/npm/packs/luxon/package.json index 33fc5b61a9..5af25e6a40 100644 --- a/npm/packs/luxon/package.json +++ b/npm/packs/luxon/package.json @@ -1,5 +1,5 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/luxon", "publishConfig": { "access": "public" @@ -7,5 +7,5 @@ "dependencies": { "luxon": "^1.21.3" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/malihu-custom-scrollbar-plugin/package.json b/npm/packs/malihu-custom-scrollbar-plugin/package.json index 17cbbcb815..bf875c7ae3 100644 --- a/npm/packs/malihu-custom-scrollbar-plugin/package.json +++ b/npm/packs/malihu-custom-scrollbar-plugin/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/malihu-custom-scrollbar-plugin", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "malihu-custom-scrollbar-plugin": "^3.1.5" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/markdown-it/package.json b/npm/packs/markdown-it/package.json index 4aa5dea7b9..966ca34354 100644 --- a/npm/packs/markdown-it/package.json +++ b/npm/packs/markdown-it/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/markdown-it", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "markdown-it": "^10.0.0" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/owl.carousel/package.json b/npm/packs/owl.carousel/package.json index f24976744b..db1ed90936 100644 --- a/npm/packs/owl.carousel/package.json +++ b/npm/packs/owl.carousel/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/owl.carousel", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "owl.carousel": "^2.3.4" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/popper.js/package.json b/npm/packs/popper.js/package.json index 2d7eb9686d..4a69a79ac3 100644 --- a/npm/packs/popper.js/package.json +++ b/npm/packs/popper.js/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/popper.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "popper.js": "^1.16.0" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/prismjs/package.json b/npm/packs/prismjs/package.json index 122548c375..036e5c568b 100644 --- a/npm/packs/prismjs/package.json +++ b/npm/packs/prismjs/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/prismjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "prismjs": "^1.17.1" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/select2/package.json b/npm/packs/select2/package.json index b68378607c..e389d17c41 100644 --- a/npm/packs/select2/package.json +++ b/npm/packs/select2/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/select2", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "select2": "^4.0.12" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/sweetalert/package.json b/npm/packs/sweetalert/package.json index 4ce2d23a30..371d4a0a71 100644 --- a/npm/packs/sweetalert/package.json +++ b/npm/packs/sweetalert/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/sweetalert", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "^2.6.0", + "@abp/core": "^2.6.2", "sweetalert": "^2.1.2" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/timeago/package.json b/npm/packs/timeago/package.json index f96e5eefa1..ec8c453933 100644 --- a/npm/packs/timeago/package.json +++ b/npm/packs/timeago/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/timeago", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "^2.6.0", + "@abp/jquery": "^2.6.2", "timeago": "^1.6.7" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/toastr/package.json b/npm/packs/toastr/package.json index 61fc01263b..596aaedf74 100644 --- a/npm/packs/toastr/package.json +++ b/npm/packs/toastr/package.json @@ -1,12 +1,12 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/toastr", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "^2.6.0", + "@abp/jquery": "^2.6.2", "toastr": "^2.1.4" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/tui-editor/package.json b/npm/packs/tui-editor/package.json index a2b06894b3..baf04a199a 100644 --- a/npm/packs/tui-editor/package.json +++ b/npm/packs/tui-editor/package.json @@ -1,15 +1,15 @@ { - "version": "2.6.0", + "version": "2.6.2", "name": "@abp/tui-editor", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/codemirror": "^2.6.0", - "@abp/highlight.js": "^2.6.0", - "@abp/jquery": "^2.6.0", - "@abp/markdown-it": "^2.6.0", + "@abp/codemirror": "^2.6.2", + "@abp/highlight.js": "^2.6.2", + "@abp/jquery": "^2.6.2", + "@abp/markdown-it": "^2.6.2", "tui-editor": "^1.4.8" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/npm/packs/utils/package.json b/npm/packs/utils/package.json index 85f6c77783..bd1c9555f4 100644 --- a/npm/packs/utils/package.json +++ b/npm/packs/utils/package.json @@ -1,6 +1,6 @@ { "name": "@abp/utils", - "version": "2.6.0", + "version": "2.6.2", "scripts": { "prepublish": "yarn install --ignore-scripts && node prepublish.js", "ng": "ng", @@ -55,5 +55,5 @@ "dependencies": { "just-compare": "^1.3.0" }, - "gitHead": "03238c9aa7d4090b7c6ae9cc8c790bc0545501eb" + "gitHead": "eb4a0e507f492e275865b72caeb02ce28d69ae56" } diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index f9e1263908..9c77b05eef 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -101,6 +101,7 @@ $projects = ( "framework/src/Volo.Abp.Sms", "framework/src/Volo.Abp.Specifications", "framework/src/Volo.Abp.TestBase", + "framework/src/Volo.Abp.TextTemplating", "framework/src/Volo.Abp.Threading", "framework/src/Volo.Abp.Timing", "framework/src/Volo.Abp.UI", diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl-PL.json similarity index 90% rename from samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl.json rename to samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl-PL.json index 9181e35186..e8629599a2 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl.json +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Permission:ProductManagement": "Zarządzanie produktami", "Permission:Products": "Produkty", diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl-PL.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl-PL.json new file mode 100644 index 0000000000..3ea7b190ee --- /dev/null +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl-PL.json @@ -0,0 +1,6 @@ +{ + "culture": "pl-PL", + "texts": { + + } +} \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl.json deleted file mode 100644 index 2ea227cbf3..0000000000 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/pl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pl", - "texts": { - - } -} \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl-PL.json similarity index 95% rename from samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl.json rename to samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl-PL.json index 5828dd8942..929b543df8 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl.json +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:ProductManagement": "Zarządzanie produktami", "Menu:Products": "Produkty", diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index 930fa03390..81a9940ecc 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -11,11 +11,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~2.6.0", - "@abp/ng.identity": "~2.6.0", - "@abp/ng.setting-management": "~2.6.0", - "@abp/ng.tenant-management": "~2.6.0", - "@abp/ng.theme.basic": "~2.6.0", + "@abp/ng.account": "~2.6.2", + "@abp/ng.identity": "~2.6.2", + "@abp/ng.setting-management": "~2.6.2", + "@abp/ng.tenant-management": "~2.6.2", + "@abp/ng.theme.basic": "~2.6.2", "@angular/animations": "~9.1.0", "@angular/common": "~9.1.0", "@angular/compiler": "~9.1.0", @@ -47,7 +47,6 @@ "karma-jasmine": "~3.0.1", "karma-jasmine-html-reporter": "^1.4.2", "ng-packagr": "^9.0.0", - "ngxs-schematic": "^1.1.9", "protractor": "~5.4.3", "ts-node": "~8.3.0", "tslint": "~6.1.0", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json similarity index 90% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json index 815bbcc83f..33412f307c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json @@ -1,5 +1,5 @@ { - "culture": "pl", + "culture": "pl-PL", "texts": { "Menu:Home": "Home", "Welcome": "Witaj", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json index f605246e7b..526a774925 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/yarn.lock b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/yarn.lock index 5fc85152d2..c1fd6c79d6 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/yarn.lock +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package.json index b5b2cae0f0..20cf0b8327 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-identityserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/yarn.lock b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/yarn.lock index 4c57ec4bc1..3d1045173a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/yarn.lock +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json index f605246e7b..526a774925 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/yarn.lock b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/yarn.lock index 4c57ec4bc1..3d1045173a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/yarn.lock +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json index f605246e7b..526a774925 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/yarn.lock b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/yarn.lock index 5fc85152d2..c1fd6c79d6 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/yarn.lock +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 06812af67d..b10de556f7 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -12,11 +12,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~2.6.0", - "@abp/ng.identity": "~2.6.0", - "@abp/ng.setting-management": "~2.6.0", - "@abp/ng.tenant-management": "~2.6.0", - "@abp/ng.theme.basic": "~2.6.0", + "@abp/ng.account": "~2.6.2", + "@abp/ng.identity": "~2.6.2", + "@abp/ng.setting-management": "~2.6.2", + "@abp/ng.tenant-management": "~2.6.2", + "@abp/ng.theme.basic": "~2.6.2", "@angular/animations": "~9.1.2", "@angular/common": "~9.1.2", "@angular/compiler": "~9.1.2", diff --git a/templates/module/angular/projects/my-project-name-config/package.json b/templates/module/angular/projects/my-project-name-config/package.json index 7e2efec6e3..7f6d7479b7 100644 --- a/templates/module/angular/projects/my-project-name-config/package.json +++ b/templates/module/angular/projects/my-project-name-config/package.json @@ -2,6 +2,6 @@ "name": "my-project-name.config", "version": "0.0.1", "peerDependencies": { - "@abp/ng.core": "~2.6.0" + "@abp/ng.core": "~2.6.2" } } diff --git a/templates/module/angular/projects/my-project-name/package.json b/templates/module/angular/projects/my-project-name/package.json index fd1072b13a..1abd7f6b4b 100644 --- a/templates/module/angular/projects/my-project-name/package.json +++ b/templates/module/angular/projects/my-project-name/package.json @@ -2,7 +2,7 @@ "name": "my-project-name", "version": "0.0.1", "dependencies": { - "@abp/ng.theme.shared": "~2.6.0", + "@abp/ng.theme.shared": "~2.6.2", "my-project-name.config": "^0.0.1" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/package.json index b5b2cae0f0..20cf0b8327 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-identityserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/yarn.lock b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/yarn.lock index 4c57ec4bc1..3d1045173a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/yarn.lock +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json index f605246e7b..526a774925 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/yarn.lock b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/yarn.lock index 4c57ec4bc1..3d1045173a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/yarn.lock +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json index f605246e7b..526a774925 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.6.2" } } \ No newline at end of file diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/yarn.lock b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/yarn.lock index 816c098b73..8b7fd5cc64 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/yarn.lock +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.0.tgz#8f5080a5eb8236ea900a06cf311d484273c05dc3" - integrity sha512-DwtF0Z2INUAgF4dZtMxJCdJuLQdwqwmvxH5LuFmde3t8+aIJmkWEifkzqAraYFEUiLORhCeH84H4UkvfL78tjw== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.0" - -"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.0.tgz#352fcb20189495aa01957264c2123dffcafc352d" - integrity sha512-dzBOl1nVNREzylPi0I1HJ08Ibr/rdkaPoFyFSWxPN7YTD+eXOdZCs+k69LvC3VLouIpcg68fjeihGKPsjH1LNg== - dependencies: - "@abp/aspnetcore.mvc.ui" "^2.6.0" - "@abp/bootstrap" "^2.6.0" - "@abp/bootstrap-datepicker" "^2.6.0" - "@abp/datatables.net-bs4" "^2.6.0" - "@abp/font-awesome" "^2.6.0" - "@abp/jquery-form" "^2.6.0" - "@abp/jquery-validation-unobtrusive" "^2.6.0" - "@abp/lodash" "^2.6.0" - "@abp/luxon" "^2.6.0" - "@abp/malihu-custom-scrollbar-plugin" "^2.6.0" - "@abp/select2" "^2.6.0" - "@abp/sweetalert" "^2.6.0" - "@abp/timeago" "^2.6.0" - "@abp/toastr" "^2.6.0" - -"@abp/aspnetcore.mvc.ui@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.0.tgz#a4f439abafa574d0f96bec8e5268157adfdf5557" - integrity sha512-k1vu49VRh/0GhzhNOuUc1UVNL90LZ16PMSlBvD5hvDifwJAVgM5jeHv7EymBMZFY42YmeXbljE78EZ9zQlWgtQ== +"@abp/aspnetcore.mvc.ui.theme.basic@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-2.6.2.tgz#c9f16291034eb68ea05b27ad3c503cb90cb1ee0a" + integrity sha512-aGKPHHRosvIONIwEfHJmSu1o61t5UxAiuQEe4vXf1+Jl9Fb/yKvOWFCMJ4BTgV+F+vNXK5OlD7u1oeWprVjKow== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^2.6.2" + +"@abp/aspnetcore.mvc.ui.theme.shared@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-2.6.2.tgz#281584070428adc693f554c051cb4290d2401956" + integrity sha512-aq946vbDmT7cRqB5SmyadINkU1bJnOvOKhf8P0ss0JWIUBWW2MS0f3N1cRfUAgP9DBP+u+uOjijnd6kI2eC7EQ== + dependencies: + "@abp/aspnetcore.mvc.ui" "^2.6.2" + "@abp/bootstrap" "^2.6.2" + "@abp/bootstrap-datepicker" "^2.6.2" + "@abp/datatables.net-bs4" "^2.6.2" + "@abp/font-awesome" "^2.6.2" + "@abp/jquery-form" "^2.6.2" + "@abp/jquery-validation-unobtrusive" "^2.6.2" + "@abp/lodash" "^2.6.2" + "@abp/luxon" "^2.6.2" + "@abp/malihu-custom-scrollbar-plugin" "^2.6.2" + "@abp/select2" "^2.6.2" + "@abp/sweetalert" "^2.6.2" + "@abp/timeago" "^2.6.2" + "@abp/toastr" "^2.6.2" + +"@abp/aspnetcore.mvc.ui@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-2.6.2.tgz#854fb357c4f8c9c9d40aff877fe00b440c82dc3e" + integrity sha512-yUthVRfgBJwYqehJqWEa8oTLuoJJKSlGvlnSQsjJrV8+BiE47rPwPVkoP3ow0gUog/jRfvFjfJsyc7H9pPekEQ== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,143 +41,143 @@ path "^0.12.7" rimraf "^3.0.0" -"@abp/bootstrap-datepicker@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.0.tgz#7f2599476a71db4cb777b479363e6103361d07bb" - integrity sha512-afytwKpGAg/Mjb2nqg6TgG4j2X7GNiC/LSM/ONfHBMVYtcpQrcnC4pd40gGej8B3N3HgvqL9hvzFoiQbxwp3Tw== +"@abp/bootstrap-datepicker@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-2.6.2.tgz#2e811e1ae6326d665004fa7e54eab571eaf7e886" + integrity sha512-bp9rCC6h/k3SK9BM6Nu+lZXYOtjuVMgD0Tuu5qZkkdgmcaDoFzFlBZgs+4HEMJzMNGop0n3cJ2YKiYmJHaUvlA== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.0.tgz#7e8b4a39f4dfd9971d41bc8489d2df535abbf83c" - integrity sha512-1Mde4lJqtBC7rqAFySn+59KmhdumzZGMy8bNpfB0Hgee1P71c2Qi5B3nnM7vIePcpqpdOoeXQsWRjVacn7xgNw== +"@abp/bootstrap@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-2.6.2.tgz#48265d0236e09d653999f6a22d80265461f32a35" + integrity sha512-lXP7QR3+3Aqsq5+IVg7J7gIhLvdqkapWRon1PkdZIgiveY3ue0MZywApJNhz/vpfUbO3t/tP4mjaiFEMd45qzQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" bootstrap "^4.3.1" -"@abp/core@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.0.tgz#1ed75753fba9adc59e6b974bc8cf03167651d1cd" - integrity sha512-1rRXGv5Ut2Sr5fP52bTTB5gR6xb9EtC62T2h1yX9y99AE64Nz0MxAy7wL/sjNdTdggRGgk0vn9lCGQjT67FnDA== +"@abp/core@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-2.6.2.tgz#7a256dd7c5707229849a36236b3df5bbbf080946" + integrity sha512-UOuWZXBxCtbKrmjqyXE5z76+E7pr0JcfKCDOmi0Au7F7qy5zU+CfYCJt4IbOQklgq0v3US2AMHj9CjBUA12Tfg== dependencies: - "@abp/utils" "^2.6.0" + "@abp/utils" "^2.6.2" -"@abp/datatables.net-bs4@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.0.tgz#190a421175c0303f971e6ef0e8697ac97ad121d0" - integrity sha512-h/q7GuATKSIeyA5gnqCoMT1HoVGSuuROysM/MNSs78kcbw2eULiEo13OiYEnHhQcCdVjDC9o/1kum1T1agqYyQ== +"@abp/datatables.net-bs4@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-2.6.2.tgz#14f35f94a01dac93306175d20d9d2270c935579d" + integrity sha512-YxqLD3XmxlECjo/Xhf7Qq7RvYDNTEUxeSTJNAUOr66DWINPbzqMyQF//q8OtpWt3lK5FRix+/W3EOqX07wb+bw== dependencies: - "@abp/datatables.net" "^2.6.0" + "@abp/datatables.net" "^2.6.2" datatables.net-bs4 "^1.10.20" -"@abp/datatables.net@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.0.tgz#d9daff70212359f9048901049422f5fe7b7d44fc" - integrity sha512-+UMnuPj0SyhTPp30AAp8knKjjrf0u0rYePTymqEc9sg0DyWyf38Q0Jf9jXQgUVkNBrR8vyivYSk+Hu2WPbDvdw== +"@abp/datatables.net@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-2.6.2.tgz#df3706dce18512773c79cb86615083763313424b" + integrity sha512-XU3U2xtmHs9ED0MpKZ0hnj/JDvIVYy+zqxRvcqNcIlUC6QBgcJE7evAMMeh55I9gOfqZgBOyND6XQIhGGzfLgg== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" datatables.net "^1.10.20" -"@abp/font-awesome@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.0.tgz#085a168e7731bb7fad4deb99ce0323edc479d564" - integrity sha512-sTRQ16NbtV8p3sz8+HLvfRUSjRRnXlz1bgvfJW6kbAphruNUdYx1kivo1OYaA1AlfE6mimVCV5X0Ibogt5Zoeg== +"@abp/font-awesome@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-2.6.2.tgz#472b831f292b59fdd34c8395f41855afdafe63e9" + integrity sha512-IkGxxC0MX7LOF0BKNURpLqsQiulCd6yAjbAkbu/TVoteO+G1qbw5+hioX3MOklF6te3xc//+HKhil+0zmd0tKQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" "@fortawesome/fontawesome-free" "^5.11.2" -"@abp/jquery-form@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.0.tgz#9c5298d21e899535b9c75e2bdbc4a3d4387ed53f" - integrity sha512-TzyFFj/ZknBW26J1FuRi0TUaeRv3Qtmr+sUtOdOF/FQ2hTY0gkWqdWikSM73BOtysoQlbSUY1saXjt35vyIMqw== +"@abp/jquery-form@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-2.6.2.tgz#56dc3e585e1d9ae8b85e8d23181f066b8017e4c0" + integrity sha512-rBXU24pqSwb+Me2W7V23jLaWuFVCzVqD1xO3Zbp8VoVMLysn7xv6FtTSNEGs8gazoYT5mAuYqZzbvYcKLrpC4w== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-form "^4.2.2" -"@abp/jquery-validation-unobtrusive@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.0.tgz#8ca83f8555181c06950538d268d38075263379ed" - integrity sha512-b762M0A4VvdNCxg4UWIZed6kbqIW2NPEUQmGu/S1hM2vZRQW5oKaTvtA4uG76YxREHgIdNzxx9vOc4YTgFTHhA== +"@abp/jquery-validation-unobtrusive@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-2.6.2.tgz#5f324d146c488068ec53e4e76e9d2b4f19c20295" + integrity sha512-TzHGyR+nQO6Vm+aAzKopuEN7CdhrZ6MXleyrFFeUodkdzvl8zay6C2GRBIdZ2vIpv0EIUySxkNCAol8QJSifNA== dependencies: - "@abp/jquery-validation" "^2.6.0" + "@abp/jquery-validation" "^2.6.2" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.0.tgz#12d2e1247b1e8af9525a7eab81244b96bda62435" - integrity sha512-Ilwy4/7doUNxTsoOtvqzansa9enax4dFTDVzVdZoY+QJDyqqDZuRYPBksY/GrD/hlDJH992J7yAi5r0VjLr5pQ== +"@abp/jquery-validation@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-2.6.2.tgz#c9d615fbae0cd5629135de425d530e20f9e4d7ed" + integrity sha512-+baPNwj4A4EXDgG+bxWI14Nn8MNFDxVt5AQXrgIUuv4o1/6Wud7OaBs7qW7guHptiAUlE7cZFUFkdKn7vsbN+g== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" jquery-validation "^1.19.1" -"@abp/jquery@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.0.tgz#df50fba8a6f1e180a5678fbb61885b12477133af" - integrity sha512-ODzknwC4J53qNLrTwFogaGqpvYL+8V424UR2KRl0cKqoy0ztBVN0lR68AypMV5ZM268YEyf7/SCvKU0iVZ0/XA== +"@abp/jquery@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-2.6.2.tgz#c9cd3e1631924aaa3d9d6874e18104abc8b93e44" + integrity sha512-tn2YlIN3qPb+9CsaM6pTo1KgknJusyxczMrsWBq3BglVgKCcMSoTXi7N0tGj927d0kLShrU8urOwNchyijt1kQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" jquery "^3.4.1" -"@abp/lodash@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.0.tgz#fae3ca61d7581c0c30a26249157fa073331a5a9d" - integrity sha512-TlbwC8ZDXC3dlxiNmpMZd6ceTQIAGzjiJpbudUFIvzgT3449IbGMUjVMGIauiKdprqVWf/b26rj9aQieSniYBQ== +"@abp/lodash@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-2.6.2.tgz#9c200b1040da55f4dac7748feea6672dbeab1a6c" + integrity sha512-XQERYW4Ff13hYO2PoFqLKmykR0BDJxkCCeVUSvj49gtaW1lyEKog/33T9Xa0ewazS6hhKjqGO0f6R11FMZOJTQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" lodash "^4.17.15" -"@abp/luxon@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.0.tgz#bb20b123adafe6271c6ee8f6a348ee9f252e0dcb" - integrity sha512-gggxzc2M8gHOxl7HzSHoxlMpyn8kflPV4KBDyscN6Va9T43yf6YQ9YBPGAeiD5fQP/xBZflVbGs3kj4TO5bAqA== +"@abp/luxon@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-2.6.2.tgz#5278e8d8abc5c524af3977557c50135395aa2f0d" + integrity sha512-T/GLdmjhsQeEpfVs/8Djm0hZqPFtMFRRR6Hi0+mFuAkEW5XwIuhApAluyBFCA1RYu9qPCZVqa9oXgNUq4TvVvg== dependencies: luxon "^1.21.3" -"@abp/malihu-custom-scrollbar-plugin@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.0.tgz#e00cfa3dc1515fd5c847ee59573902bd3dd6ef5d" - integrity sha512-VyXAdiBXc9a2XwRJaKIOcui38VgPHX1I+alS7d1FVNZswRiP/7j9IBpz08rugqSgNnin+1FF6oKT0qVSrLF4Mg== +"@abp/malihu-custom-scrollbar-plugin@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-2.6.2.tgz#0f849ad03ab713109fada47d5e5367bafa99c2dc" + integrity sha512-GtADJJUfvisKIXTwF3xEXWWBNYI549dtlytITV1ZnMB9V3vvVbtNlJqMMxQnOGB0V77x+RKWzDx+0N7L1GCHRg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.0.tgz#0dc98e60329b0097c8517a783035acd7ffe1d018" - integrity sha512-00E90+QQRQq6YZz7RJtPycrC9OjEX0q2kvcDhuFi6lpHLVakVn2CFZkdJm81H+3eWPjweCsZXbT5VDL+azOZww== +"@abp/select2@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-2.6.2.tgz#699d35efbe388f716a2aa1822bc0177d93e19859" + integrity sha512-Otiw711Jx3XmKB8VMQGn5XGHP88xXCCP/ucw3ILVjJIt6VZFPMAIpQPAKGQuEcYQiv8Ohlnw4J/OMV/y7V6ZKg== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" select2 "^4.0.12" -"@abp/sweetalert@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.0.tgz#6a745c3037199e6c68c54d9af734a133225c1a0c" - integrity sha512-gy8pgBwzDhFIzLffSuvLxJokkcUvqNqnYA6P4cHLgh7R+YRRk+Emv5nCWQLHEIGNHMgnQrQXuDkoXJzLStWlvg== +"@abp/sweetalert@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-2.6.2.tgz#ab7ca9630d10bebaf80e7edf59380643a2e0834c" + integrity sha512-ft+bQynMdJI21LreOgsGra5KJKHtPubxdUTCLPshg5J1TUSLp74ScohJEDtOIzATmo4BCyEBLtqBEpxYpYkgDQ== dependencies: - "@abp/core" "^2.6.0" + "@abp/core" "^2.6.2" sweetalert "^2.1.2" -"@abp/timeago@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.0.tgz#66c12464363d37a17b0a2ec6671ca78ca7a71466" - integrity sha512-9MWtQqY5KPW8UNpIu1x9O5k25flbBk+wgaa+YwIkZ3pi0ZjKXGPiNEmB/lhl2EVbR09agEqaB+kTp+9tTVouUA== +"@abp/timeago@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-2.6.2.tgz#cdb624b3d50bfc7ef719498bcecde786a60fecc2" + integrity sha512-ifeA6KIIwpoBmN5idcdOaHE1SBg3bsZ9rF47KP3+zOox5ZtpLLg3K1UxGQyKIkGg/RjYrg7WIYdR67HQBXslHA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" timeago "^1.6.7" -"@abp/toastr@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.0.tgz#2f90ecec62b43656ea0cb6915d5d59e82ff01ef4" - integrity sha512-u6yVUl2AVQsPAAP3YjCnH+Zm3CPbB1T7XRyNblW/sFsHOrzklK/chjrnW7XQbnlDkoQHuYXXu8gvNeeaoy9Y9g== +"@abp/toastr@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-2.6.2.tgz#00fa37b43b6fa2b68f41dfa6ffc5fa4ca47dcf88" + integrity sha512-6qAYYz8DdNDDxkgzf0O3HOkXZbD2ZoUS6HbuH5KeFXxX0q8qhmHAJd3+4N/2851uGnT/KeFateJb68SZP5XQKA== dependencies: - "@abp/jquery" "^2.6.0" + "@abp/jquery" "^2.6.2" toastr "^2.1.4" -"@abp/utils@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.0.tgz#ca174b19f74594a0ae8d64f56b032f07b9168ed1" - integrity sha512-ABQUgFHbv8ktGR+tKx/1s+5a9JHuBwUVAy/Hn8OjbEDoSK73UHoapfFSShF/x12dWDKLcpvErmeiASiXb+soCw== +"@abp/utils@^2.6.2": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.6.2.tgz#dba9e7c0aa1531fae6c5ff22fcbdfb4d98c576e3" + integrity sha512-MqExfPA4FRYORsskVG7kbkAwzs9z4XaMaZ75XkoahD+FyZtm1e0miEEoDvwrdye9jgL/FrWeShWNE49GY7PvpA== dependencies: just-compare "^1.3.0" diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json new file mode 100644 index 0000000000..3ea7b190ee --- /dev/null +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl-PL.json @@ -0,0 +1,6 @@ +{ + "culture": "pl-PL", + "texts": { + + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json deleted file mode 100644 index 2ea227cbf3..0000000000 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/pl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pl", - "texts": { - - } -} \ No newline at end of file