Browse Source

Merge branch 'dev' into akin/added-metaDescription-field-in-blog

pull/3432/head
Alper Ebicoglu 7 years ago
parent
commit
e73f9e10df
  1. 4
      docs/en/API/Dynamic-CSharp-API-Clients.md
  2. 4
      docs/en/Authorization.md
  3. 4
      docs/en/Best-Practices/Entity-Framework-Core-Integration.md
  4. 4
      docs/en/Best-Practices/MongoDB-Integration.md
  5. 1
      docs/en/CLI.md
  6. 2
      docs/en/Modules/Docs.md
  7. 4
      docs/en/UI/Angular/Track-By-Service.md
  8. 4
      docs/en/UI/AspNetCore/Bundling-Minification.md
  9. 62
      docs/en/UI/Common/Utils/Linked-List.md
  10. 9
      docs/en/docs-nav.json
  11. 4
      docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md
  12. 4
      docs/zh-Hans/Authorization.md
  13. 4
      docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md
  14. 6
      docs/zh-Hans/Best-Practices/MongoDB-Integration.md
  15. 12
      docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
  16. 2
      framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs
  17. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  18. 10
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
  19. 135
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs
  20. 14
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs
  21. 1
      framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs
  22. 4
      framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs
  23. 17
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs
  24. 18
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs
  25. 1
      npm/ng-packs/package.json
  26. 33
      npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts
  27. 16
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  28. 15
      npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html
  29. 27
      npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts
  30. 12
      npm/ng-packs/yarn.lock
  31. 2
      npm/packs/utils/abp.resourcemapping.js
  32. 2
      npm/packs/utils/package.json
  33. 3
      npm/packs/utils/projects/utils/ng-package.json

4
docs/en/API/Dynamic-CSharp-API-Clients.md

@ -69,7 +69,7 @@ public class MyClientAppModule : AbpModule
}
````
See the "RemoteServiceOptions" section below for more detailed configuration.
See the "AbpRemoteServiceOptions" section below for more detailed configuration.
## Usage
@ -104,7 +104,7 @@ While you can inject `IBookAppService` like above to use the client proxy, you c
## Configuration
### RemoteServiceOptions
### AbpRemoteServiceOptions
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:

4
docs/en/Authorization.md

@ -339,10 +339,10 @@ A permission value provider should return one of the following values from the `
- `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return.
- `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission.
Once a provider is defined, it should be added to the `PermissionOptions` as shown below:
Once a provider is defined, it should be added to the `AbpPermissionOptions` as shown below:
```csharp
Configure<PermissionOptions>(options =>
Configure<AbpPermissionOptions>(options =>
{
options.ValueProviders.Add<SystemAdminPermissionValueProvider>();
});

4
docs/en/Best-Practices/Entity-Framework-Core-Integration.md

@ -107,10 +107,10 @@ public static class IdentityDbContextModelBuilderExtensions
````
* **Do** call `b.ConfigureByConvention();` for each entity mapping (as shown above).
* **Do** create a **configuration options** class by inheriting from the `ModelBuilderConfigurationOptions`. Example:
* **Do** create a **configuration options** class by inheriting from the `AbpModelBuilderConfigurationOptions`. Example:
````C#
public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)

4
docs/en/Best-Practices/MongoDB-Integration.md

@ -90,11 +90,11 @@ public static class AbpIdentityMongoDbContextExtensions
}
```
- **Do** create a **configuration options** class by inheriting from the `MongoModelBuilderConfigurationOptions`. Example:
- **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example:
```c#
public class IdentityMongoModelBuilderConfigurationOptions
: MongoModelBuilderConfigurationOptions
: AbpMongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)

1
docs/en/CLI.md

@ -60,6 +60,7 @@ abp new Acme.BookStore
* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.
* `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. You can set your own connection string if you don't want to use the default. Be aware that the default database provider is `SQL Server`, therefore you can only enter connection string for SQL Server!
### add-package

2
docs/en/Modules/Docs.md

@ -134,7 +134,7 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<PermissionOptions>(options =>
Configure<AbpPermissionOptions>(options =>
{
options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>();
});

4
docs/en/UI/Angular/Track-By-Service.md

@ -111,7 +111,3 @@ class DemoComponent {
trackByTenantAccountId = trackByDeep<Item>('tenant', 'account', 'id');
}
```
## What's Next?
* [Linked List (Doubly)](./Linked-List.md)

4
docs/en/UI/AspNetCore/Bundling-Minification.md

@ -246,10 +246,10 @@ public class MyPrismjsStyleExtension : BundleContributor
}
````
Then you can configure `BundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`.
Then you can configure `AbpBundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`.
````csharp
Configure<BundleContributorOptions>(options =>
Configure<AbpBundleContributorOptions>(options =>
{
options
.Extensions<PrismjsStyleBundleContributor>()

62
docs/en/UI/Angular/Linked-List.md → docs/en/UI/Common/Utils/Linked-List.md

@ -130,7 +130,11 @@ list.addTail({ x: 3 });
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addAfter({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x);
list.addAfter(
{ x: 0 },
2,
(value, searchedValue) => value.x === searchedValue
);
// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}
```
@ -164,7 +168,11 @@ list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]);
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addManyAfter([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x);
list.addManyAfter(
[{ x: 4 }, { x: 5 }],
2,
(value, searchedValue) => value.x === searchedValue
);
// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3}
```
@ -203,7 +211,11 @@ list.addTail({ x: 3 });
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addBefore({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x);
list.addBefore(
{ x: 0 },
2,
(value, searchedValue) => value.x === searchedValue
);
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3}
```
@ -237,7 +249,11 @@ list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]);
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addManyBefore([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x);
list.addManyBefore(
[{ x: 4 }, { x: 5 }],
2,
(value, searchedValue) => value.x === searchedValue
);
// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3}
```
@ -388,7 +404,9 @@ list.add({ x: 3 }).tail();
// {"x":1} <-> {"x":2} <-> {"x":3}
list.add({ x: 0 }).after({ x: 2 }, (v1, v2) => v1.x === v2.x);
list
.add({ x: 0 })
.after(2, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}
```
@ -429,7 +447,9 @@ list.add({ x: 3 }).tail();
// {"x":1} <-> {"x":2} <-> {"x":3}
list.add({ x: 0 }).before({ x: 2 }, (v1, v2) => v1.x === v2.x);
list
.add({ x: 0 })
.before(2, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3}
```
@ -543,7 +563,9 @@ list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addMany([{ x: 4 }, { x: 5 }]).after({ x: 2 }, (v1, v2) => v1.x === v2.x);
list
.addMany([{ x: 4 }, { x: 5 }])
.after(2, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3}
```
@ -579,7 +601,9 @@ list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":2} <-> {"x":3}
list.addMany([{ x: 4 }, { x: 5 }]).before({ x: 2 }, (v1, v2) => v1.x === v2.x);
list
.addMany([{ x: 4 }, { x: 5 }])
.before(2, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3}
```
@ -779,7 +803,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x);
list.dropByValue(0, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}
```
@ -813,7 +837,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x);
list.dropByValue(0, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":3}
```
@ -921,7 +945,9 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
list.drop().byValue({ x: 0 }, (v1, v2) => v1.x === v2.x);
list
.drop()
.byValue(0, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}
```
@ -957,7 +983,9 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
list.drop().byValueAll({ x: 0 }, (v1, v2) => v1.x === v2.x);
list
.drop()
.byValueAll(0, (value, searchedValue) => value.x === searchedValue);
// {"x":1} <-> {"x":2} <-> {"x":3}
```
@ -1145,11 +1173,11 @@ list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]);
// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
const i0 = indexOf({ x: 1 }, (v1, v2) => v1.x === v2.x);
const i1 = indexOf({ x: 2 }, (v1, v2) => v1.x === v2.x);
const i2 = indexOf({ x: 3 }, (v1, v2) => v1.x === v2.x);
const i3 = indexOf({ x: 0 }, (v1, v2) => v1.x === v2.x);
const i4 = indexOf({ x: 4 }, (v1, v2) => v1.x === v2.x);
const i0 = indexOf(1, (value, searchedValue) => value.x === searchedValue);
const i1 = indexOf(2, (value, searchedValue) => value.x === searchedValue);
const i2 = indexOf(3, (value, searchedValue) => value.x === searchedValue);
const i3 = indexOf(0, (value, searchedValue) => value.x === searchedValue);
const i4 = indexOf(4, (value, searchedValue) => value.x === searchedValue);
/*
i0 === 0

9
docs/en/docs-nav.json

@ -344,10 +344,15 @@
{
"text": "TrackByService",
"path": "UI/Angular/Track-By-Service.md"
},
}
]
},
{
"text": "Common",
"items": [
{
"text": "Linked List (Doubly)",
"path": "UI/Angular/Linked-List.md"
"path": "UI/Common/Utils/Linked-List.md"
}
]
}

4
docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md

@ -69,7 +69,7 @@ public class MyClientAppModule : AbpModule
}
````
查看下面的"RemoteServiceOptions"章节获取更多详细配置.
查看下面的"AbpRemoteServiceOptions"章节获取更多详细配置.
## 使用
@ -104,7 +104,7 @@ public class MyService : ITransientDependency
## 配置
### RemoteServiceOptions
### AbpRemoteServiceOptions
默认情况下`AbpRemoteServiceOptions`从`appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如:

4
docs/zh-Hans/Authorization.md

@ -340,10 +340,10 @@ public class SystemAdminPermissionValueProvider : PermissionValueProvider
* `PermissionGrantResult.Prohibited` 禁止授权用户,任何一个授权值提供程序返回了 `Prohibited`, 那么其他的提供程序返回的值都不再重要.
* `PermissionGrantResult.Undefined` 代表当前无法确定是否授予或禁止权限, 返回`UnDefined`由其他权限值提供程序检查权限.
定义`Provider`后将其添加到 `PermissionOptions`,如下所示:
定义`Provider`后将其添加到 `AbpPermissionOptions`,如下所示:
````csharp
Configure<PermissionOptions>(options =>
Configure<AbpPermissionOptions>(options =>
{
options.ValueProviders.Add<SystemAdminPermissionValueProvider>();
});

4
docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md

@ -106,10 +106,10 @@ public static class IdentityDbContextModelBuilderExtensions
````
* **推荐** 为每个Enttiy映射调用 `b.ConfigureByConvention();`(如上所示).
* **推荐** 通过继承 `ModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 例如:
* **推荐** 通过继承 `AbpModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 例如:
````C#
public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions
public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions
{
public IdentityModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema)

6
docs/zh-Hans/Best-Practices/MongoDB-Integration.md

@ -1,4 +1,4 @@
## MongoDB 集成
## MongoDB 集成
* **推荐** 为每个模块定义一个独立的 `MongoDbContext` 接口与实现类.
@ -90,11 +90,11 @@ public static class AbpIdentityMongoDbContextExtensions
}
```
- **推荐** 通过继承 `MongoModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 示例:
- **推荐** 通过继承 `AbpMongoModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 示例:
```c#
public class IdentityMongoModelBuilderConfigurationOptions
: MongoModelBuilderConfigurationOptions
: AbpMongoModelBuilderConfigurationOptions
{
public IdentityMongoModelBuilderConfigurationOptions()
: base(AbpIdentityConsts.DefaultDbTablePrefix)

12
docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md

@ -115,7 +115,7 @@ public class MyWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<BundlingOptions>(options =>
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
@ -153,7 +153,7 @@ public class MyWebExtensionModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<BundlingOptions>(options =>
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
@ -191,7 +191,7 @@ public class MyExtensionGlobalStyleContributor : BundleContributor
然后你可以按照下面的代码使用这个贡献者:
````C#
services.Configure<BundlingOptions>(options =>
services.Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
@ -248,10 +248,10 @@ public class MyPrismjsStyleExtension : BundleContributor
}
````
然后你可以配置 `BundleContributorOptions` 去扩展已存在的 `PrismjsStyleBundleContributor`.
然后你可以配置 `AbpBundleContributorOptions` 去扩展已存在的 `PrismjsStyleBundleContributor`.
````csharp
Configure<BundleContributorOptions>(options =>
Configure<AbpBundleContributorOptions>(options =>
{
options
.Extensions<PrismjsStyleBundleContributor>()
@ -319,7 +319,7 @@ namespace MyCompany.MyProject
例如:
````c#
services.Configure<BundlingOptions>(options =>
services.Configure<AbpBundlingOptions>(options =>
{
options
.StyleBundles

2
framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs

@ -8,7 +8,7 @@ namespace AutoMapper
{
public static IMappingExpression<TSource, TDestination> MapExtraProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression,
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both)
MappingPropertyDefinitionChecks? definitionChecks = null)
where TDestination : IHasExtraProperties
where TSource : IHasExtraProperties
{

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -218,6 +218,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp new Acme.BookStore -t module --no-ui");
sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\"");
sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\"");
sb.AppendLine(" abp new Acme.BookStore --connection-string \"Server=myServerName\\myInstanceName;Database=myDatabase;User Id=myUsername;Password=myPassword\"");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");

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

@ -70,9 +70,17 @@ namespace Volo.Abp.IdentityModel
}
var tokenResponse = await GetTokenResponse(discoveryResponse, configuration);
if (tokenResponse.IsError)
{
throw new AbpException($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}");
if (tokenResponse.ErrorDescription != null)
{
throw new AbpException($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}");
}
var rawError = tokenResponse.Raw;
var withoutInnerException = rawError.Split(new string[] { "<eof/>" }, StringSplitOptions.RemoveEmptyEntries);
throw new AbpException(withoutInnerException[0]);
}
return tokenResponse.AccessToken;

135
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
using Volo.Abp.Data;
@ -25,7 +24,7 @@ namespace Volo.Abp.ObjectExtending
public static void MapExtraPropertiesTo<TSource, TDestination>(
[NotNull] this TSource source,
[NotNull] TDestination destination,
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both)
MappingPropertyDefinitionChecks? definitionChecks = null)
where TSource : IHasExtraProperties
where TDestination : IHasExtraProperties
{
@ -58,7 +57,7 @@ namespace Volo.Abp.ObjectExtending
public static void MapExtraPropertiesTo<TSource, TDestination>(
[NotNull] Dictionary<string, object> sourceDictionary,
[NotNull] Dictionary<string, object> destinationDictionary,
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both)
MappingPropertyDefinitionChecks? definitionChecks = null)
where TSource : IHasExtraProperties
where TDestination : IHasExtraProperties
{
@ -90,7 +89,7 @@ namespace Volo.Abp.ObjectExtending
[NotNull] Type destinationType,
[NotNull] Dictionary<string, object> sourceDictionary,
[NotNull] Dictionary<string, object> destinationDictionary,
MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both)
MappingPropertyDefinitionChecks? definitionChecks = null)
{
Check.AssignableTo<IHasExtraProperties>(sourceType, nameof(sourceType));
Check.AssignableTo<IHasExtraProperties>(destinationType, nameof(destinationType));
@ -98,78 +97,120 @@ namespace Volo.Abp.ObjectExtending
Check.NotNull(destinationDictionary, nameof(destinationDictionary));
var sourceObjectExtension = ObjectExtensionManager.Instance.GetOrNull(sourceType);
if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Source) &&
sourceObjectExtension == null)
{
return;
}
var destinationObjectExtension = ObjectExtensionManager.Instance.GetOrNull(destinationType);
if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Destination) &&
destinationObjectExtension == null)
{
return;
}
if (definitionChecks == MappingPropertyDefinitionChecks.None)
foreach (var keyValue in sourceDictionary)
{
foreach (var keyValue in sourceDictionary)
if (CanMapProperty(
keyValue.Key,
sourceObjectExtension,
destinationObjectExtension,
definitionChecks))
{
destinationDictionary[keyValue.Key] = keyValue.Value;
}
}
else if (definitionChecks == MappingPropertyDefinitionChecks.Source)
{
Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null");
}
//TODO: Move these methods to a class like ObjectExtensionHelper
public static bool CanMapProperty<TSource, TDestination>(
[NotNull] string propertyName,
MappingPropertyDefinitionChecks? definitionChecks = null)
{
return CanMapProperty(
typeof(TSource),
typeof(TDestination),
propertyName,
definitionChecks
);
}
public static bool CanMapProperty(
[NotNull] Type sourceType,
[NotNull] Type destinationType,
[NotNull] string propertyName,
MappingPropertyDefinitionChecks? definitionChecks = null)
{
Check.AssignableTo<IHasExtraProperties>(sourceType, nameof(sourceType));
Check.AssignableTo<IHasExtraProperties>(destinationType, nameof(destinationType));
Check.NotNull(propertyName, nameof(propertyName));
var sourceObjectExtension = ObjectExtensionManager.Instance.GetOrNull(sourceType);
var destinationObjectExtension = ObjectExtensionManager.Instance.GetOrNull(destinationType);
return CanMapProperty(
propertyName,
sourceObjectExtension,
destinationObjectExtension,
definitionChecks);
}
foreach (var property in sourceObjectExtension.GetProperties())
private static bool CanMapProperty(
[NotNull] string propertyName,
[CanBeNull] ObjectExtensionInfo sourceObjectExtension,
[CanBeNull] ObjectExtensionInfo destinationObjectExtension,
MappingPropertyDefinitionChecks? definitionChecks = null)
{
Check.NotNull(propertyName, nameof(propertyName));
if (definitionChecks != null)
{
if (definitionChecks.Value.HasFlag(MappingPropertyDefinitionChecks.Source))
{
if (!sourceDictionary.ContainsKey(property.Name))
if (sourceObjectExtension == null)
{
continue;
return false;
}
destinationDictionary[property.Name] = sourceDictionary[property.Name];
if (!sourceObjectExtension.HasProperty(propertyName))
{
return false;
}
}
}
else if (definitionChecks == MappingPropertyDefinitionChecks.Destination)
{
Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null");
foreach (var keyValue in sourceDictionary)
if (definitionChecks.Value.HasFlag(MappingPropertyDefinitionChecks.Destination))
{
if (!destinationObjectExtension.HasProperty(keyValue.Key))
if (destinationObjectExtension == null)
{
continue;
return false;
}
destinationDictionary[keyValue.Key] = keyValue.Value;
if (!destinationObjectExtension.HasProperty(propertyName))
{
return false;
}
}
return true;
}
else if (definitionChecks == MappingPropertyDefinitionChecks.Both)
else
{
Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null");
Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null");
var sourcePropertyDefinition = sourceObjectExtension?.GetPropertyOrNull(propertyName);
var destinationPropertyDefinition = destinationObjectExtension?.GetPropertyOrNull(propertyName);
foreach (var property in sourceObjectExtension.GetProperties())
if (sourcePropertyDefinition != null)
{
if (!sourceDictionary.ContainsKey(property.Name))
if (destinationPropertyDefinition != null)
{
continue;
return true;
}
if (!destinationObjectExtension.HasProperty(property.Name))
if (sourcePropertyDefinition.CheckPairDefinitionOnMapping == false)
{
continue;
return true;
}
destinationDictionary[property.Name] = sourceDictionary[property.Name];
}
}
else
{
throw new NotImplementedException(definitionChecks + " was not implemented!");
else if (destinationPropertyDefinition != null)
{
if (destinationPropertyDefinition.CheckPairDefinitionOnMapping == false)
{
return true;
}
}
return false;
}
}
}
}
}

14
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs

@ -15,6 +15,20 @@ namespace Volo.Abp.ObjectExtending
[NotNull]
public Type Type { get; }
/// <summary>
/// Indicates whether to check the other side of the object mapping
/// if it explicitly defines the property. This property is used in;
///
/// * .MapExtraPropertiesTo() extension method.
/// * .MapExtraProperties() configuration for the AutoMapper.
///
/// It this is true, these methods check if the mapping object
/// has defined the property using the <see cref="ObjectExtensionManager"/>.
///
/// Default: null (unspecified, uses the default logic).
/// </summary>
public bool? CheckPairDefinitionOnMapping { get; set; }
[NotNull]
public Dictionary<object, object> Configuration { get; }

1
framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs

@ -19,6 +19,7 @@ namespace Volo.Abp.ObjectExtending
ObjectExtensionManager.Instance
.AddOrUpdateProperty<ExtensibleTestPerson, string>("Name")
.AddOrUpdateProperty<ExtensibleTestPerson, int>("Age")
.AddOrUpdateProperty<ExtensibleTestPerson, string>("NoPairCheck", options => options.CheckPairDefinitionOnMapping = false)
.AddOrUpdateProperty<ExtensibleTestPersonDto, string>("Name")
.AddOrUpdateProperty<ExtensibleTestPersonDto, int>("ChildCount");
});

4
framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs

@ -16,7 +16,8 @@ namespace Volo.Abp.ObjectExtending
.SetProperty("Name", "John")
.SetProperty("Age", 42)
.SetProperty("ChildCount", 2)
.SetProperty("Sex", "male");
.SetProperty("Sex", "male")
.SetProperty("NoPairCheck", "test-value");
_personDto = new ExtensibleTestPersonDto()
.SetProperty("ExistingDtoProperty", "existing-value");
@ -31,6 +32,7 @@ namespace Volo.Abp.ObjectExtending
_personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination
_personDto.HasProperty("ChildCount").ShouldBeFalse(); //Not defined in the source
_personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
_personDto.GetProperty<string>("NoPairCheck").ShouldBe("test-value"); //CheckPairDefinitionOnMapping = false
_personDto.GetProperty<string>("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values
}

17
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs

@ -1,7 +1,22 @@
namespace Volo.Blogging.Files
using System.Security.Cryptography;
using Volo.Abp.Threading;
namespace Volo.Blogging.Files
{
public class RawFileDto
{
public byte[] Bytes { get; set; }
public bool IsFileEmpty => Bytes == null || Bytes.Length == 0;
public RawFileDto()
{
}
public static RawFileDto EmptyResult()
{
return new RawFileDto() {Bytes = new byte[0]};
}
}
}

18
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs

@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Validation;
@ -12,9 +13,11 @@ namespace Volo.Blogging.Files
public class FileAppService : BloggingAppServiceBase, IFileAppService
{
public BlogFileOptions Options { get; }
private readonly ILogger<FileAppService> _logger;
public FileAppService(IOptions<BlogFileOptions> options)
public FileAppService(IOptions<BlogFileOptions> options, ILogger<FileAppService> logger)
{
_logger = logger;
Options = options.Value;
}
@ -35,12 +38,13 @@ namespace Volo.Blogging.Files
var filePath = Path.Combine(Options.FileUploadLocalFolder, name);
return Task.FromResult(
new RawFileDto
{
Bytes = File.ReadAllBytes(filePath)
}
);
if (File.Exists(filePath))
{
return Task.FromResult(new RawFileDto {Bytes = File.ReadAllBytes(filePath)});
}
_logger.LogError($"Cannot find the file {filePath}");
return Task.FromResult(RawFileDto.EmptyResult());
}
public virtual Task<FileUploadOutputDto> CreateAsync(FileUploadInputDto input)

1
npm/ng-packs/package.json

@ -34,6 +34,7 @@
"@abp/ng.tenant-management.config": "~2.3.0",
"@abp/ng.theme.basic": "~2.3.0",
"@abp/ng.theme.shared": "^2.3.0",
"@abp/utils": "~2.3.0",
"@angular-builders/jest": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.21",
"@angular-devkit/build-ng-packagr": "~0.803.21",

33
npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts

@ -1,10 +1,10 @@
import { getInitialData, localeInitializer } from '../utils';
import { Injector } from '@angular/core';
import { Spectator, createComponentFactory } from '@ngneat/spectator/jest';
import { Component } from '@angular/core';
import { Component, Injector } from '@angular/core';
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { of } from 'rxjs';
import { GetAppConfiguration } from '../actions';
import { getInitialData, localeInitializer } from '../utils';
@Component({
selector: 'abp-dummy',
@ -14,7 +14,10 @@ export class DummyComponent {}
describe('InitialUtils', () => {
let spectator: Spectator<DummyComponent>;
const createComponent = createComponentFactory({ component: DummyComponent, mocks: [Store] });
const createComponent = createComponentFactory({
component: DummyComponent,
mocks: [Store],
});
beforeEach(() => (spectator = createComponent()));
@ -25,7 +28,8 @@ describe('InitialUtils', () => {
const store = spectator.get(Store);
const dispatchSpy = jest.spyOn(store, 'dispatch');
injectorSpy.mockReturnValue(store);
injectorSpy.mockReturnValueOnce(store);
injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => false });
dispatchSpy.mockReturnValue(of('test'));
expect(typeof getInitialData(injector)).toBe('function');
@ -34,6 +38,23 @@ describe('InitialUtils', () => {
});
});
describe('#checkAccessToken', () => {
test('should call logOut fn of OAuthService when token is valid and current user not found', async () => {
const injector = spectator.get(Injector);
const injectorSpy = jest.spyOn(injector, 'get');
const store = spectator.get(Store);
const dispatchSpy = jest.spyOn(store, 'dispatch');
const logOutFn = jest.fn();
injectorSpy.mockReturnValueOnce(store);
injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => true, logOut: logOutFn });
dispatchSpy.mockReturnValue(of({ currentUser: { id: null } }));
getInitialData(injector)();
expect(logOutFn).toHaveBeenCalled();
});
});
describe('#localeInitializer', () => {
test('should resolve registerLocale', async () => {
const injector = spectator.get(Injector);

16
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -3,17 +3,31 @@ import { Injector } from '@angular/core';
import { Store } from '@ngxs/store';
import { GetAppConfiguration } from '../actions/config.actions';
import differentLocales from '../constants/different-locales';
import { ApplicationConfiguration } from '../models/application-configuration';
import { tap } from 'rxjs/operators';
import { ConfigState } from '../states/config.state';
import { OAuthService } from 'angular-oauth2-oidc';
export function getInitialData(injector: Injector) {
const fn = () => {
const store: Store = injector.get(Store);
return store.dispatch(new GetAppConfiguration()).toPromise();
return store
.dispatch(new GetAppConfiguration())
.pipe(tap(res => checkAccessToken(store, injector)))
.toPromise();
};
return fn;
}
function checkAccessToken(store: Store, injector: Injector) {
const oAuth = injector.get(OAuthService);
if (oAuth.hasValidAccessToken() && !store.selectSnapshot(ConfigState.getDeep('currentUser.id'))) {
oAuth.logOut();
}
}
export function localeInitializer(injector: Injector) {
const fn = () => {
const store: Store = injector.get(Store);

15
npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html

@ -195,8 +195,19 @@
</ng-template>
<ng-template #currentUser>
<li *ngIf="(currentUser$ | async)?.isAuthenticated" class="nav-item">
<div ngbDropdown class="dropdown" #currentUserDropdown="ngbDropdown" display="static">
<li class="nav-item">
<ng-template #loginBtn>
<a role="button" class="nav-link" routerLink="/account/login">{{
'AbpAccount::Login' | abpLocalization
}}</a>
</ng-template>
<div
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn"
ngbDropdown
class="dropdown"
#currentUserDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"

27
npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts

@ -1,4 +1,12 @@
import { animate, animation, trigger, state, style, transition, useAnimation } from '@angular/animations';
import {
animate,
animation,
trigger,
state,
style,
transition,
useAnimation,
} from '@angular/animations';
export const collapseY = animation(
[
@ -9,7 +17,10 @@ export const collapseY = animation(
);
export const collapseYWithMargin = animation(
[style({ 'margin-top': '0' }), animate('{{ time }} {{ easing }}', style({ 'margin-top': '-100%' }))],
[
style({ 'margin-top': '0' }),
animate('{{ time }} {{ easing }}', style({ 'margin-left': '-100%' })),
],
{
params: { time: '500ms', easing: 'ease' },
},
@ -32,7 +43,10 @@ export const expandY = animation(
);
export const expandYWithMargin = animation(
[style({ 'margin-top': '-100%' }), animate('{{ time }} {{ easing }}', style({ 'margin-top': '0' }))],
[
style({ 'margin-top': '-100%' }),
animate('{{ time }} {{ easing }}', style({ 'margin-top': '0' })),
],
{
params: { time: '500ms', easing: 'ease' },
},
@ -63,11 +77,14 @@ export const collapseWithMargin = trigger('collapseWithMargin', [
]);
export const collapseLinearWithMargin = trigger('collapseLinearWithMargin', [
state('collapsed', style({ 'margin-top': '-100%' })),
state('collapsed', style({ 'margin-top': '-100vh' })),
state('expanded', style({ 'margin-top': '0' })),
transition(
'expanded => collapsed',
useAnimation(collapseYWithMargin, { params: { time: '200ms', easing: 'linear' } }),
),
transition('collapsed => expanded', useAnimation(expandYWithMargin, { params: { time: '250ms', easing: 'linear' } })),
transition(
'collapsed => expanded',
useAnimation(expandYWithMargin, { params: { time: '250ms', easing: 'linear' } }),
),
]);

12
npm/ng-packs/yarn.lock

@ -120,6 +120,13 @@
chart.js "^2.9.3"
tslib "^1.9.0"
"@abp/utils@~2.3.0":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-2.3.1.tgz#728b978d598a98b643562250be54c92988b986e5"
integrity sha512-sZ1nrl8pV1Zx3WAqHRUQ2V3aJlsjaq1ggg2VIPYY4FjCYHFZ3L+Fkq5cPPZb/MczG1YZpAQiMtCJ/+8slvPHHQ==
dependencies:
just-compare "^1.3.0"
"@angular-builders/jest@^8.2.0":
version "8.3.2"
resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-8.3.2.tgz#669c350c9df2e3afc9ae048c5067759c36113728"
@ -9578,11 +9585,6 @@ pretty-format@^24.0.0, pretty-format@^24.3.0, pretty-format@^24.8.0, pretty-form
ansi-styles "^3.2.0"
react-is "^16.8.4"
primeicons@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/primeicons/-/primeicons-2.0.0.tgz#ec3e5a8d4d04a09309225a5a57deb911e08dc0d3"
integrity sha512-GJTCeMSQU8UU1GqbsaDrg/IH+b/vSinJQl52NVpdJ7sShYLZA8Eq6jLF48Ye3N/dQloGrE07i7XsZvxQ9pNbqg==
private@^0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"

2
npm/packs/utils/abp.resourcemapping.js

@ -1,5 +1,5 @@
module.exports = {
mappings: {
'@node_modules/@abp/utils/bundles/*': '@libs/abp/utils/',
'@node_modules/@abp/utils/dist/bundles/*.*': '@libs/abp/utils/',
},
};

2
npm/packs/utils/package.json

@ -1,6 +1,6 @@
{
"name": "@abp/utils",
"version": "2.3.1",
"version": "2.3.2",
"scripts": {
"prepublish": "yarn install --ignore-scripts && node prepublish.js",
"ng": "ng",

3
npm/packs/utils/projects/utils/ng-package.json

@ -3,7 +3,8 @@
"dest": "../../dist",
"deleteDestPath": true,
"lib": {
"entryFile": "src/public-api.ts"
"entryFile": "src/public-api.ts",
"umdId": "abp.utils.common"
},
"whitelistedNonPeerDependencies": ["just-compare"]
}

Loading…
Cancel
Save