Browse Source

Merge pull request #8407 from abpframework/auto-merge/rel-4-3/220

Merge branch dev with rel-4.3
pull/8419/head
Mehmet Erim 5 years ago
committed by GitHub
parent
commit
adcbcec458
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/en/Tutorials/Part-3.md
  2. 2
      docs/en/Tutorials/Part-9.md
  3. 2
      docs/en/UI/Angular/Entity-Action-Extensions.md
  4. 2
      docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md
  5. 6
      docs/en/UI/Angular/Modal.md
  6. 218
      docs/en/UI/Angular/Page-Component.md
  7. 2
      docs/en/UI/Angular/Permission-Management-Component-Replacement.md
  8. 9
      docs/en/docs-nav.json
  9. 4
      docs/pt-BR/Tutorials/Angular/Part-II.md
  10. 4
      docs/zh-Hans/Tutorials/Part-3.md
  11. 2
      docs/zh-Hans/UI/Angular/Permission-Management-Component-Replacement.md
  12. 7
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs
  13. 36
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs
  14. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs
  15. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs
  16. 16
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/CreateMediaInputWithStream.cs
  17. 2
      modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/IMediaDescriptorAdminAppService.cs
  18. 10
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs
  19. 10
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/CmsKitAdminHttpApiModule.cs
  20. 27
      modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs
  21. 13
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs
  22. 4
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.html
  23. 2
      npm/ng-packs/packages/components/page/src/page.component.html
  24. 11
      npm/ng-packs/packages/components/page/src/page.component.ts
  25. 6
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html
  26. 2
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  27. 2
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  28. 2
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html
  29. 2
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html
  30. 1
      npm/ng-packs/packages/theme-shared/src/lib/components/index.ts
  31. 16
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-close.directive.ts
  32. 13
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
  33. 2
      npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts
  34. 2
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  35. 59
      npm/ng-packs/yarn.lock

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

@ -709,7 +709,7 @@ Open `/src/app/book/book.component.html` and make the following changes:
<ng-template #abpBody> </ng-template> <ng-template #abpBody> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ '::Close' | abpLocalization }}}%} {%{{{ '::Close' | abpLocalization }}}%}
</button> </button>
</ng-template> </ng-template>
@ -844,7 +844,7 @@ Also replace `<ng-template #abpFooter> </ng-template>` with the following code p
````html ````html
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ '::Close' | abpLocalization }}}%} {%{{{ '::Close' | abpLocalization }}}%}
</button> </button>

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

@ -792,7 +792,7 @@ Open the `/src/app/author/author.component.html` and replace the content as belo
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ '::Close' | abpLocalization }}}%} {%{{{ '::Close' | abpLocalization }}}%}
</button> </button>

2
docs/en/UI/Angular/Entity-Action-Extensions.md

@ -180,7 +180,7 @@ Let's employ dependency injection to extend the functionality of `IdentityModule
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ 'AbpUi::Close' | abpLocalization }}}%} {%{{{ 'AbpUi::Close' | abpLocalization }}}%}
</button> </button>
</ng-template> </ng-template>

2
docs/en/UI/Angular/How-Replaceable-Components-Work-with-Extensions.md

@ -215,7 +215,7 @@ Open the generated `src/app/my-role/my-role.component.html` file and replace its
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%} {%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button> </button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{%{{{ <abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{%{{{

6
docs/en/UI/Angular/Modal.md

@ -6,7 +6,7 @@ The `abp-modal` provides some additional benefits:
- It is **flexible**. You can pass header, body, footer templates easily by adding the templates to the `abp-modal` content. It can also be implemented quickly. - It is **flexible**. You can pass header, body, footer templates easily by adding the templates to the `abp-modal` content. It can also be implemented quickly.
- Provides several inputs be able to customize the modal and several outputs be able to listen to some events. - Provides several inputs be able to customize the modal and several outputs be able to listen to some events.
- Automatically detects the close button which has a `#abpClose` template variable and closes the modal when pressed this button. - Automatically detects the close button which has a `abpClose` directive attached to and closes the modal when pressed this button.
- Automatically detects the `abp-button` and triggers its loading spinner when the `busy` input value of the modal component is true. - Automatically detects the `abp-button` and triggers its loading spinner when the `busy` input value of the modal component is true.
- Automatically checks if the form inside the modal **has changed, but not saved**. It warns the user by displaying a [confirmation popup](Confirmation-Service) in this case when a user tries to close the modal or refresh/close the tab of the browser. - Automatically checks if the form inside the modal **has changed, but not saved**. It warns the user by displaying a [confirmation popup](Confirmation-Service) in this case when a user tries to close the modal or refresh/close the tab of the browser.
@ -47,7 +47,7 @@ You can add the `abp-modal` to your component very quickly. See an example:
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>Close</button> <button type="button" class="btn btn-secondary" abpClose>Close</button>
</ng-template> </ng-template>
</abp-modal> </abp-modal>
``` ```
@ -116,7 +116,7 @@ See an example form inside a modal:
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
Cancel Cancel
</button> </button>

218
docs/en/UI/Angular/Page-Component.md

@ -0,0 +1,218 @@
# Page Component
ABP provides a component that wraps your content with some built-in components to reduce the amount of code you need to write.
If the template of a component looks as follows, you can utilize the `abp-page` component.
Let's look at the following example without `abp-page` component.
`dashboard.component.ts`
```html
<div class="row entry-row">
<div class="col-auto">
<h1 class="content-header-title">{{ '::Dashboard' | abpLocalization }}</h1>
</div>
<div id="breadcrumb" class="col-lg-auto pl-lg-0">
<abp-breadcrumb></abp-breadcrumb>
</div>
<div class="col">
<abp-page-toolbar [record]="data"></abp-page-toolbar>
</div>
</div>
<div id="dashboard-id">
<!-- dashboard content here -->
</div>
```
## Page Parts
PageComponent divides the template shown above into three parts, `title`, `breadcrumb`, `toolbar`. Each can be configured separately. There, also, is an enum exported from the package that describes each part.
```javascript
export enum PageParts {
title = 'PageTitleContainerComponent',
breadcrumb = 'PageBreadcrumbContainerComponent',
toolbar = 'PageToolbarContainerComponent',
}
// You can import this enum from -> import { PageParts } from '@abp/ng.components/page';
```
## Usage
Firstly, you need to import `PageModule` from `@abp/ng.components/page` as follows:
`dashboard.module.ts`
```javascript
import { PageModule } from '@abp/ng.components/page';
import { DashboardComponent } from './dashboard.component';
@NgModule({
declarations: [DashboardComponent],
imports: [PageModule]
})
export class DashboardModule {}
```
And change the template of `dashboard.component.ts` to the following:
```html
<abp-page [title]="'::Dashboard' | abpLocalization" [toolbar]="data">
<div id="dashboard-id">
<!-- .... -->
</div>
</abp-page>
```
## Inputs
* title: `string`: Will be be rendered within `h1.content-header-title`. If not provided, the parent `div` will not be rendered
* breadcrumb: `boolean`: Determines whether to render `abp-breadcrumb`. Default is `true`.
* toolbar: `any`: Will be passed into `abp-page-toolbar` component through `record` input. If your page does not contain `abp-page-toolbar`, you can simply omit this field.
## Overriding template
If you need to replace the template of any part, you can use the following sub-components.
```html
<abp-page>
<abp-page-title-container>
<div class="col">
<h2>Custom Title</h2>
</div>
</abp-page-title-container>
<abp-page-breacrumb-container>
<div class="col">
<my-breadcrumb></my-breadcrumb>
</div>
</abp-page-breacrumb-container>
<abp-page-toolbar-container>
<div class="col">
<!-- ... -->
</div>
</abp-page-toolbar-container>
</abp-page>
```
You do not have to provide them all. You can just use which one you need to replace. These components have priority over the inputs declared above. If you use these components, you can omit the inputs.
## PagePartDirective
`PageModule` provides a structural directive that is used internally within `PageComponent` and can also be used externally.
`PageComponent` employs this directive internally as follows:
```html
<div class="col-lg-auto pl-lg-0" *abpPagePart="pageParts.breadcrumb">
<abp-breadcrumb></abp-breadcrumb>
</div>
```
It also can take a context input as follows:
```html
<div class="col" *abpPagePart="pageParts.toolbar; context: toolbarData">
<abp-page-toolbar [record]="toolbarData"></abp-page-toolbar>
</div>
```
Its render strategy can be provided through Angular's Dependency Injection system.
It expects a service through the `PAGE_RENDER_STRATEGY` injection token that implements the following interface.
```javascript
interface PageRenderStrategy {
shouldRender(type?: string): boolean | Observable<boolean>;
onInit?(type?: string, injector?: Injector, context?: any): void;
onDestroy?(type?: string, injector?: Injector, context?: any): void;
onContextUpdate?(change?: SimpleChange): void;
}
```
* `shouldRender` (required): It takes a string input named `type` and expects a `boolean` or `Observable<boolean>` in return.
* `onInit` (optional): Will be called when the directive is initiated. Three inputs will be passed into this method.
* `type`: type of the page part
* `injector`: injector of the directive which could be used to retrieve anything from directive's DI tree.
* `context`: whatever context is available at the initialization phase.
* `onDestroy` (optional): Will be called when the directive is destroyed. The parameters are the same with `onInit`
* `onContextUpdate` (optional): Will be called when the context is updated.
* `change`: changes of the `context` will be passed through this method.
Let's see everything in action.
```javascript
import { 
PageModule,
PageRenderStrategy,
PageParts,
PAGE_RENDER_STRATEGY
} from '@abp/ng.components/page';
@Injectable()
export class MyPageRenderStrategy implements PageRenderStrategy {
shouldRender(type: string) {
// meaning everything but breadcrumb and custom-part will be rendered
return type !== PageParts.breadcrumb && type !== 'custom-part';
}
/**
* shouldRender can also return an Observable<boolean> which means
* an async service can be used within.
constructor(private service: SomeAsyncService) {}
shouldRender(type: string) {
return this.service.checkTypeAsync(type).pipe(map(val => val.isTrue()));
}
*/
onInit(type: string, injector: Injector, context: any) {
// this method will be called in ngOnInit of the directive
}
onDestroy(type: string, injector: Injector, context: any) {
// this method will be called in ngOnDestroy of the directive
}
onContextUpdate?(change?: SimpleChange) {
// this method will be called everytime context is updated within the directive
}
}
@Component({
selector: 'app-dashboard',
template: `
<abp-page [title]="'::Dashboard' | abpLocalization">
<abp-page-toolbar-container>
<button>New Dashboard</button>
</abp-page-toolbar-container>
<div class="dashboard-content">
<h3 *abpPagePart="'custom-part'"> Inner Title </h3>
</div>
</abp-page>
`
})
export class DashboardComponent {}
@NgModule({
imports: [PageModule],
declarations: [DashboardComponent],
providers: [
{
provide: PAGE_RENDER_STRATEGY,
useClass: MyPageRenderStrategy,
}
]
})
export class DashboardModule {}
```
## See Also
- [Page Toolbar Extensions for Angular UI](./Page-Page-Toolbar-Extensions.md)

2
docs/en/UI/Angular/Permission-Management-Component-Replacement.md

@ -459,7 +459,7 @@ Open the generated `permission-management.component.html` in `src/app/permission
</div> </div>
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%} {%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button> </button>
<abp-button iconClass="fa fa-check" (click)="submit()">{%{{{ <abp-button iconClass="fa fa-check" (click)="submit()">{%{{{

9
docs/en/docs-nav.json

@ -882,6 +882,15 @@
] ]
} }
] ]
},
{
"text": "Components",
"items": [
{
"text": "Page",
"path": "UI/Angular/Page-Component.md"
}
]
} }
] ]
}, },

4
docs/pt-BR/Tutorials/Angular/Part-II.md

@ -90,7 +90,7 @@ Abra o `book-list.component.html`e adicione o `abp-modal`para mostrar / ocultar
<ng-template #abpBody> </ng-template> <ng-template #abpBody> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
Cancel Cancel
</button> </button>
</ng-template> </ng-template>
@ -276,7 +276,7 @@ Abra o `book-list.component.html`e adicione um `abp-button`para salvar o formul
```html ```html
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
Cancel Cancel
</button> </button>
<button class="btn btn-primary" (click)="save()"> <button class="btn btn-primary" (click)="save()">

4
docs/zh-Hans/Tutorials/Part-3.md

@ -718,7 +718,7 @@ export class BookComponent implements OnInit {
<ng-template #abpBody> </ng-template> <ng-template #abpBody> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ '::Close' | abpLocalization }}}%} {%{{{ '::Close' | abpLocalization }}}%}
</button> </button>
</ng-template> </ng-template>
@ -859,7 +859,7 @@ export class BookComponent implements OnInit {
````html ````html
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ '::Close' | abpLocalization }}}%} {%{{{ '::Close' | abpLocalization }}}%}
</button> </button>

2
docs/zh-Hans/UI/Angular/Permission-Management-Component-Replacement.md

@ -459,7 +459,7 @@ function getPermissions(groups: PermissionManagement.Group[]): PermissionManagem
</div> </div>
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%} {%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button> </button>
<abp-button iconClass="fa fa-check" (click)="submit()">{%{{{ <abp-button iconClass="fa fa-check" (click)="submit()">{%{{{

7
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs

@ -115,13 +115,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters
} }
} }
} }
else
{
postedFiles.Add(new RemoteStreamContent(request.Body)
{
ContentType = request.ContentType
});
}
} }
} }
} }

36
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs

@ -53,17 +53,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
return null; return null;
} }
if (value is IRemoteStreamContent remoteStreamContent) return new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json);
{
var content = new StreamContent(remoteStreamContent.GetStream());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(remoteStreamContent.ContentType);
content.Headers.ContentLength = remoteStreamContent.ContentLength;
return content;
}
else
{
return new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json);
}
} }
private static HttpContent GenerateFormPostData(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> methodArguments) private static HttpContent GenerateFormPostData(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> methodArguments)
@ -80,7 +70,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
if (parameters.Any(x => x.BindingSourceId == ParameterBindingSources.FormFile)) if (parameters.Any(x => x.BindingSourceId == ParameterBindingSources.FormFile))
{ {
var postDataBuilder = new MultipartFormDataContent(); var formData = new MultipartFormDataContent();
foreach (var parameter in parameters) foreach (var parameter in parameters)
{ {
var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameter); var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameter);
@ -91,32 +81,42 @@ namespace Volo.Abp.Http.Client.DynamicProxying
if (value is IRemoteStreamContent remoteStreamContent) if (value is IRemoteStreamContent remoteStreamContent)
{ {
var streamContent = new StreamContent(remoteStreamContent.GetStream()); var stream = remoteStreamContent.GetStream();
if (stream.CanSeek)
{
stream.Position = 0;
}
var streamContent = new StreamContent(stream);
if (!remoteStreamContent.ContentType.IsNullOrWhiteSpace()) if (!remoteStreamContent.ContentType.IsNullOrWhiteSpace())
{ {
streamContent.Headers.ContentType = new MediaTypeHeaderValue(remoteStreamContent.ContentType); streamContent.Headers.ContentType = new MediaTypeHeaderValue(remoteStreamContent.ContentType);
} }
postDataBuilder.Add(streamContent, parameter.Name, parameter.Name); formData.Add(streamContent, parameter.Name, parameter.Name);
} }
else if (value is IEnumerable<IRemoteStreamContent> remoteStreamContents) else if (value is IEnumerable<IRemoteStreamContent> remoteStreamContents)
{ {
foreach (var content in remoteStreamContents) foreach (var content in remoteStreamContents)
{ {
var streamContent = new StreamContent(content.GetStream()); var stream = content.GetStream();
if (stream.CanSeek)
{
stream.Position = 0;
}
var streamContent = new StreamContent(stream);
if (!content.ContentType.IsNullOrWhiteSpace()) if (!content.ContentType.IsNullOrWhiteSpace())
{ {
streamContent.Headers.ContentType = new MediaTypeHeaderValue(content.ContentType); streamContent.Headers.ContentType = new MediaTypeHeaderValue(content.ContentType);
} }
postDataBuilder.Add(streamContent, parameter.Name, parameter.Name); formData.Add(streamContent, parameter.Name, parameter.Name);
} }
} }
else else
{ {
postDataBuilder.Add(new StringContent(value.ToString(), Encoding.UTF8), parameter.Name); formData.Add(new StringContent(value.ToString(), Encoding.UTF8), parameter.Name);
} }
} }
return postDataBuilder; return formData;
} }
else else
{ {

6
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs

@ -25,11 +25,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters
[HttpPost] [HttpPost]
[Route("Upload")] [Route("Upload")]
public async Task<string> UploadAsync([FromBody]IRemoteStreamContent streamContent) public async Task<string> UploadAsync(IRemoteStreamContent file)
{ {
using (var reader = new StreamReader(streamContent.GetStream())) using (var reader = new StreamReader(file.GetStream()))
{ {
return await reader.ReadToEndAsync() + ":" + streamContent.ContentType; return await reader.ReadToEndAsync() + ":" + file.ContentType;
} }
} }
} }

8
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs

@ -1,5 +1,6 @@
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
@ -25,8 +26,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters
var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("UploadAsync")); await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("UploadAsync"));
memoryStream.Position = 0; memoryStream.Position = 0;
requestMessage.Content = new StreamContent(memoryStream);
requestMessage.Content.Headers.Add("Content-Type", "application/rtf"); var streamContent = new StreamContent(memoryStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/rtf");
requestMessage.Content = new MultipartFormDataContent {{streamContent, "file", "file"}};
var response = await Client.SendAsync(requestMessage); var response = await Client.SendAsync(requestMessage);

16
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/CreateMediaInputWithStream.cs

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Content;
using Volo.Abp.Validation;
using Volo.CmsKit.MediaDescriptors;
namespace Volo.CmsKit.Admin.MediaDescriptors
{
public class CreateMediaInputWithStream
{
[Required]
[DynamicStringLength(typeof(MediaDescriptorConsts), nameof(MediaDescriptorConsts.MaxNameLength))]
public string Name { get; set; }
public IRemoteStreamContent File { get; set; }
}
}

2
modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/MediaDescriptors/IMediaDescriptorAdminAppService.cs

@ -6,7 +6,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{ {
public interface IMediaDescriptorAdminAppService : IApplicationService public interface IMediaDescriptorAdminAppService : IApplicationService
{ {
Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream); Task<MediaDescriptorDto> CreateAsync(string entityType, CreateMediaInputWithStream inputStream);
Task DeleteAsync(Guid id); Task DeleteAsync(Guid id);
} }

10
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs

@ -1,11 +1,9 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.BlobStoring; using Volo.Abp.BlobStoring;
using Volo.Abp.GlobalFeatures; using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.MediaDescriptors; using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Permissions;
namespace Volo.CmsKit.Admin.MediaDescriptors namespace Volo.CmsKit.Admin.MediaDescriptors
{ {
@ -29,16 +27,16 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore; MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore;
} }
public virtual async Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream) public virtual async Task<MediaDescriptorDto> CreateAsync(string entityType, CreateMediaInputWithStream inputStream)
{ {
var definition = await MediaDescriptorDefinitionStore.GetAsync(inputStream.EntityType); var definition = await MediaDescriptorDefinitionStore.GetAsync(entityType);
/* TODO: Shouldn't CreatePolicies be a dictionary and we check for inputStream.EntityType? */ /* TODO: Shouldn't CreatePolicies be a dictionary and we check for inputStream.EntityType? */
await CheckAnyOfPoliciesAsync(definition.CreatePolicies); await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
using (var stream = inputStream.GetStream()) using (var stream = inputStream.File.GetStream())
{ {
var newEntity = await MediaDescriptorManager.CreateAsync(inputStream.EntityType, inputStream.Name, inputStream.ContentType, inputStream.ContentLength ?? 0); var newEntity = await MediaDescriptorManager.CreateAsync(entityType, inputStream.Name, inputStream.File.ContentType, inputStream.File.ContentLength ?? 0);
await MediaContainer.SaveAsync(newEntity.Id.ToString(), stream); await MediaContainer.SaveAsync(newEntity.Id.ToString(), stream);
await MediaDescriptorRepository.InsertAsync(newEntity); await MediaDescriptorRepository.InsertAsync(newEntity);

10
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/CmsKitAdminHttpApiModule.cs

@ -1,5 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.CmsKit.Admin.MediaDescriptors;
namespace Volo.CmsKit.Admin namespace Volo.CmsKit.Admin
{ {
@ -16,5 +18,13 @@ namespace Volo.CmsKit.Admin
mvcBuilder.AddApplicationPartIfNotExists(typeof(CmsKitAdminHttpApiModule).Assembly); mvcBuilder.AddApplicationPartIfNotExists(typeof(CmsKitAdminHttpApiModule).Assembly);
}); });
} }
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateMediaInputWithStream));
});
}
} }
} }

27
modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs

@ -25,10 +25,10 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
} }
[HttpPost] [HttpPost]
[NonAction] [Route("{entityType}")]
public virtual Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream) public virtual Task<MediaDescriptorDto> CreateAsync(string entityType, CreateMediaInputWithStream inputStream)
{ {
return MediaDescriptorAdminAppService.CreateAsync(inputStream); return MediaDescriptorAdminAppService.CreateAsync(entityType, inputStream);
} }
[HttpDelete] [HttpDelete]
@ -37,26 +37,5 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{ {
return MediaDescriptorAdminAppService.DeleteAsync(id); return MediaDescriptorAdminAppService.DeleteAsync(id);
} }
[HttpPost]
[Route("{entityType}")]
public virtual async Task<IActionResult> UploadAsync(string entityType, IFormFile file)
{
if (file == null)
{
return BadRequest();
}
var inputStream = new CreateMediaInputStream(file.OpenReadStream())
{
EntityType = entityType,
ContentType = file.ContentType,
Name = file.FileName
};
var mediaDescriptorDto = await MediaDescriptorAdminAppService.CreateAsync(inputStream);
return StatusCode((int)HttpStatusCode.Created, mediaDescriptorDto);
}
} }
} }

13
modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs

@ -3,6 +3,7 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Content;
using Volo.CmsKit.Admin.MediaDescriptors; using Volo.CmsKit.Admin.MediaDescriptors;
using Xunit; using Xunit;
@ -31,14 +32,14 @@ namespace Volo.CmsKit.MediaDescriptors
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(mediaContent)); using var stream = new MemoryStream(Encoding.UTF8.GetBytes(mediaContent));
var inputStream = new CreateMediaInputStream(stream) var media = await _mediaDescriptorAdminAppService.CreateAsync(_cmsKitTestData.Media_1_EntityType, new CreateMediaInputWithStream
{ {
ContentType = mediaType,
Name = mediaName, Name = mediaName,
EntityType = _cmsKitTestData.Media_1_EntityType File = new RemoteStreamContent(stream)
}; {
ContentType = mediaType
var media = await _mediaDescriptorAdminAppService.CreateAsync(inputStream); }
});
media.ShouldNotBeNull(); media.ShouldNotBeNull();
} }

4
npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.html

@ -3,7 +3,7 @@
<div class="card-body px-5"> <div class="card-body px-5">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<span style="font-size: 0.8em;" class="text-uppercase text-muted">{{ <span style="font-size: 0.8em" class="text-uppercase text-muted">{{
'AbpUiMultiTenancy::Tenant' | abpLocalization 'AbpUiMultiTenancy::Tenant' | abpLocalization
}}</span }}</span
><br /> ><br />
@ -47,7 +47,7 @@
</form> </form>
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary"> <button abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }} {{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button> </button>
<abp-button <abp-button

2
npm/ng-packs/packages/components/page/src/page.component.html

@ -1,4 +1,4 @@
<div class="row entry-row"> <div class="row entry-row" *ngIf="shouldRenderRow">
<ng-container *ngIf="customTitle; else defaultTitleTemplate"> <ng-container *ngIf="customTitle; else defaultTitleTemplate">
<ng-content select="abp-page-title-container"></ng-content> <ng-content select="abp-page-title-container"></ng-content>
</ng-container> </ng-container>

11
npm/ng-packs/packages/components/page/src/page.component.ts

@ -37,4 +37,15 @@ export class PageComponent {
@ContentChild(PageBreadcrumbContainerComponent) @ContentChild(PageBreadcrumbContainerComponent)
customBreadcrumb: PageBreadcrumbContainerComponent; customBreadcrumb: PageBreadcrumbContainerComponent;
@ContentChild(PageToolbarContainerComponent) customToolbar: PageToolbarContainerComponent; @ContentChild(PageToolbarContainerComponent) customToolbar: PageToolbarContainerComponent;
get shouldRenderRow() {
return !!(
this.title ||
this.toolbarVisible ||
this.breadcrumbVisible ||
this.customTitle ||
this.customBreadcrumb ||
this.customToolbar
);
}
} }

6
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html

@ -82,8 +82,8 @@
{{ {{
item.displayText?.resourceName + '::' + item.displayText?.name item.displayText?.resourceName + '::' + item.displayText?.name
| abpLocalization | abpLocalization
}}</option }}
> </option>
</select> </select>
<ng-container <ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }" *ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
@ -111,7 +111,7 @@
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary"> <button abpClose type="button" class="btn btn-secondary">
{{ 'AbpFeatureManagement::Cancel' | abpLocalization }} {{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
</button> </button>
<abp-button <abp-button

2
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html

@ -31,7 +31,7 @@
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }} {{ 'AbpIdentity::Cancel' | abpLocalization }}
</button> </button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{ <abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{

2
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -74,7 +74,7 @@
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }} {{ 'AbpIdentity::Cancel' | abpLocalization }}
</button> </button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{ <abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{

2
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html

@ -94,7 +94,7 @@
</div> </div>
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }} {{ 'AbpIdentity::Cancel' | abpLocalization }}
</button> </button>
<abp-button iconClass="fa fa-check" (click)="submit()">{{ <abp-button iconClass="fa fa-check" (click)="submit()">{{

2
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html

@ -40,7 +40,7 @@
</ng-template> </ng-template>
<ng-template #abpFooter> <ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary"> <button abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }} {{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button> </button>
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="isDisabledSaveButton">{{ <abp-button iconClass="fa fa-check" (click)="save()" [disabled]="isDisabledSaveButton">{{

1
npm/ng-packs/packages/theme-shared/src/lib/components/index.ts

@ -6,6 +6,7 @@ export * from './http-error-wrapper/http-error-wrapper.component';
export * from './loader-bar/loader-bar.component'; export * from './loader-bar/loader-bar.component';
export * from './loading/loading.component'; export * from './loading/loading.component';
export * from './modal/modal.component'; export * from './modal/modal.component';
export * from './modal/modal-close.directive';
export * from './sort-order-icon/sort-order-icon.component'; export * from './sort-order-icon/sort-order-icon.component';
export * from './table-empty-message/table-empty-message.component'; export * from './table-empty-message/table-empty-message.component';
export * from './table/table.component'; export * from './table/table.component';

16
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-close.directive.ts

@ -0,0 +1,16 @@
import { Directive, HostListener, Optional } from '@angular/core';
import { ModalComponent } from './modal.component';
@Directive({ selector: '[abpClose]' })
export class ModalCloseDirective {
constructor(@Optional() private modal: ModalComponent) {
if (!modal) {
console.error('Please use abpClose within an abp-modal');
}
}
@HostListener('click')
onClick() {
this.modal?.close();
}
}

13
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

@ -11,6 +11,7 @@ import {
Output, Output,
TemplateRef, TemplateRef,
ViewChild, ViewChild,
isDevMode,
} from '@angular/core'; } from '@angular/core';
import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { fromEvent, Subject } from 'rxjs'; import { fromEvent, Subject } from 'rxjs';
@ -78,6 +79,9 @@ export class ModalComponent implements OnDestroy {
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) @ContentChild(ButtonComponent, { static: false, read: ButtonComponent })
abpSubmit: ButtonComponent; abpSubmit: ButtonComponent;
/**
* @deprecated will be removed in v5.0
*/
@ContentChild('abpClose', { static: false, read: ElementRef }) @ContentChild('abpClose', { static: false, read: ElementRef })
abpClose: ElementRef<any>; abpClose: ElementRef<any>;
@ -203,6 +207,7 @@ export class ModalComponent implements OnDestroy {
setTimeout(() => { setTimeout(() => {
if (!this.abpClose) return; if (!this.abpClose) return;
this.warnForDeprecatedClose();
fromEvent(this.abpClose.nativeElement, 'click') fromEvent(this.abpClose.nativeElement, 'click')
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe(() => this.close()); .subscribe(() => this.close());
@ -210,4 +215,12 @@ export class ModalComponent implements OnDestroy {
this.init.emit(); this.init.emit();
} }
private warnForDeprecatedClose() {
if (isDevMode()) {
console.warn(
'Please use abpClose directive instead of #abpClose template variable. #abpClose will be removed in v5.0',
);
}
}
} }

2
npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts

@ -51,7 +51,7 @@ describe('ModalComponent', () => {
<ng-template #abpFooter> <ng-template #abpFooter>
<div class="footer"> <div class="footer">
<button id="abp-close" #abpClose></button> <button id="abp-close" abpClose></button>
<abp-button>Submit</abp-button> <abp-button>Submit</abp-button>
</div> </div>
</ng-template> </ng-template>

2
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -37,6 +37,7 @@ import { THEME_SHARED_ROUTE_PROVIDERS } from './providers/route.provider';
import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token';
import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token'; import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token';
import { DateParserFormatter } from './utils/date-parser-formatter'; import { DateParserFormatter } from './utils/date-parser-formatter';
import { ModalCloseDirective } from './components/modal/modal-close.directive';
const declarationsWithExports = [ const declarationsWithExports = [
BreadcrumbComponent, BreadcrumbComponent,
@ -55,6 +56,7 @@ const declarationsWithExports = [
NgxDatatableListDirective, NgxDatatableListDirective,
LoadingDirective, LoadingDirective,
TableSortDirective, TableSortDirective,
ModalCloseDirective,
]; ];
@NgModule({ @NgModule({

59
npm/ng-packs/yarn.lock

@ -2,37 +2,14 @@
# yarn lockfile v1 # yarn lockfile v1
"@abp/ng.account@~4.3.0-rc.1":
version "4.3.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/ng.account/-/ng.account-4.3.0-rc.1.tgz#e9d9e3807b0f4f3d9729adb14b62142b8a95dbd3"
integrity sha512-CPPSVoB937oF9vIMjqiXFQoHQAqqn4O4CYb+70gIkh3I182c40SpJD4kA+kRlmtAggBgXMQyodTfJ15qvrRKmg==
dependencies:
"@abp/ng.theme.shared" "~3.3.2"
tslib "^2.0.0"
"@abp/ng.components@~4.3.0-rc.1": "@abp/ng.components@~4.3.0-rc.1":
version "4.3.0-rc.1" version "4.3.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/ng.components/-/ng.components-4.3.0-rc.1.tgz#7142e476577fe74429b0eb0f0f9af5955a3aea3f" resolved "https://registry.yarnpkg.com/@abp/ng.components/-/ng.components-4.3.0-rc.1.tgz#7142e476577fe74429b0eb0f0f9af5955a3aea3f"
integrity sha512-JjRRbE9RHh8X82PKLO1VKsEMui+mCNzGgkmBVL1qwTrlLcfUW6IYjCVXUxQX67e6waHVT+cIFFWWk00zfVEMJw== integrity sha512-JjRRbE9RHh8X82PKLO1VKsEMui+mCNzGgkmBVL1qwTrlLcfUW6IYjCVXUxQX67e6waHVT+cIFFWWk00zfVEMJw==
dependencies: dependencies:
ng-zorro-antd "^11.0.0" ng-zorro-antd "^11.0.0"
tslib "^2.0.0"
"@abp/ng.core@~3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-3.3.2.tgz#53d6d8721d2432ee98688e678fc43ecba1f2de26"
integrity sha512-JOp16y4Uvfa4QIiBcb4LIPLoV5qBE5NhPvmu4/xzz/oSHChLmhHGdOqWVwRil4EMkL4A8QsSoAb06Rs2OckSJw==
dependencies:
"@abp/utils" "^3.3.1"
"@angular/localize" "~10.0.10"
"@ngxs/router-plugin" "^3.7.0"
"@ngxs/storage-plugin" "^3.7.0"
"@ngxs/store" "^3.7.0"
angular-oauth2-oidc "^10.0.0"
just-clone "^3.1.0"
just-compare "^1.3.0"
snq "^1.0.3"
ts-toolbelt "6.15.4"
tslib "^2.0.0" tslib "^2.0.0"
"@abp/ng.core@~4.3.0-rc.1": "@abp/ng.core@~4.3.0-rc.1":
@ -42,6 +19,8 @@
dependencies: dependencies:
"@abp/utils" "^4.2.2" "@abp/utils" "^4.2.2"
"@angular/localize" "~10.0.10" "@angular/localize" "~10.0.10"
"@ngxs/router-plugin" "^3.7.0"
"@ngxs/storage-plugin" "^3.7.0"
"@ngxs/store" "^3.7.0" "@ngxs/store" "^3.7.0"
angular-oauth2-oidc "^10.0.0" angular-oauth2-oidc "^10.0.0"
just-clone "^3.1.0" just-clone "^3.1.0"
@ -76,14 +55,15 @@
tslib "^2.0.0" tslib "^2.0.0"
"@abp/ng.schematics@~4.3.0-rc.1": "@abp/ng.schematics@~4.3.0-rc.1":
version "4.3.0-rc.1" version "4.3.0-rc.1-1"
resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.3.0-rc.1.tgz#1a71b35df7ecb1b92520b0fda8654b81eb48ee15" resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.3.0-rc.1-1.tgz#70897e5be3ea308b1a535241ddbf08ad730be1cf"
integrity sha512-7QHDFo4WHRhTeA5Hyh7Ni/7bnCJHz9vRKvxp/b1Vhw+s0aWu3nyBmWZB66atA+LLl6SeqDmO1WDJ3vTMgHGh1g== integrity sha512-YKnsx4q4OrUBJl8VbzHORlnPT1iEv6y72soe/xWqasL8i1UIEAUQjLx7ZDZCMWq+Dm+v7EgMU/yOjcNpdL3Eng==
dependencies: dependencies:
"@angular-devkit/core" "~11.0.2" "@angular-devkit/core" "~11.0.2"
"@angular-devkit/schematics" "~11.0.2" "@angular-devkit/schematics" "~11.0.2"
got "^11.5.2" got "^11.5.2"
jsonc-parser "^2.3.0" jsonc-parser "^2.3.0"
should-quote "^1.0.0"
typescript "~3.9.2" typescript "~3.9.2"
"@abp/ng.setting-management@~4.3.0-rc.1": "@abp/ng.setting-management@~4.3.0-rc.1":
@ -112,20 +92,6 @@
"@abp/ng.theme.shared" "~4.3.0-rc.1" "@abp/ng.theme.shared" "~4.3.0-rc.1"
tslib "^2.0.0" tslib "^2.0.0"
"@abp/ng.theme.shared@~3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-3.3.2.tgz#fd15b8690d9b5da950c8d4cb22fa0292b020ead1"
integrity sha512-EFDnfAiAx+ISDLPAffnunxKb4K/TVO/m/9O1fLiGtGVj1R14oq0gv3gdB1Oq0IxFS6V6M+ehocowIrXlO2HcrA==
dependencies:
"@abp/ng.core" "~3.3.2"
"@fortawesome/fontawesome-free" "^5.14.0"
"@ng-bootstrap/ng-bootstrap" "^7.0.0"
"@ngx-validate/core" "^0.0.12"
"@swimlane/ngx-datatable" "^17.1.0"
bootstrap "^4.5.0"
chart.js "^2.9.3"
tslib "^2.0.0"
"@abp/ng.theme.shared@~4.3.0-rc.1": "@abp/ng.theme.shared@~4.3.0-rc.1":
version "4.3.0-rc.1" version "4.3.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.3.0-rc.1.tgz#1442443baf2a70832f0bd0f04a2103a731b7b521" resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.3.0-rc.1.tgz#1442443baf2a70832f0bd0f04a2103a731b7b521"
@ -134,19 +100,12 @@
"@abp/ng.core" "~4.3.0-rc.1" "@abp/ng.core" "~4.3.0-rc.1"
"@fortawesome/fontawesome-free" "^5.14.0" "@fortawesome/fontawesome-free" "^5.14.0"
"@ng-bootstrap/ng-bootstrap" "^7.0.0" "@ng-bootstrap/ng-bootstrap" "^7.0.0"
"@ngx-validate/core" "^0.0.13" "@ngx-validate/core" "^0.0.12"
"@swimlane/ngx-datatable" "^17.1.0" "@swimlane/ngx-datatable" "^17.1.0"
bootstrap "~4.6.0" bootstrap "~4.6.0"
chart.js "^2.9.3" chart.js "^2.9.3"
tslib "^2.0.0" tslib "^2.0.0"
"@abp/utils@^3.3.1":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-3.3.2.tgz#c68e78c7f90284b03a2f4ae3bcf25bf77b2edc31"
integrity sha512-IRXPgPMYOzMelz+tbThdZvTQmHTG76OJvr+5bI4UTr2ROUPMDnJzLJV6Xjk1eN/esM0yZhYu7eyXoNQbgI4OZg==
dependencies:
just-compare "^1.3.0"
"@abp/utils@^4.2.2": "@abp/utils@^4.2.2":
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.2.2.tgz#7afdbbd14246ce06c074109c51d01588596805a9" resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.2.2.tgz#7afdbbd14246ce06c074109c51d01588596805a9"

Loading…
Cancel
Save