Browse Source

Merge remote-tracking branch 'abpframework/dev' into docs

pull/3831/head
liangshiwei 6 years ago
parent
commit
bf3830884f
  1. 17
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 52
      docs/en/Localization.md
  3. 8
      docs/en/Object-Extensions.md
  4. 11
      framework/Volo.Abp.sln
  5. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  6. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs
  7. 178
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs
  8. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs
  9. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs
  10. 23
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs
  11. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs
  12. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs
  13. 36
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs
  14. 25
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs
  15. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs
  16. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs
  17. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs
  18. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ICachedObjectExtensionsDtoService.cs
  19. 21
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs
  20. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionConfigurationDto.cs
  21. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs
  22. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
  23. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
  24. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  25. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  26. 31
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/AbpPageToolbarOptions.cs
  27. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/IPageToolbarContributor.cs
  28. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/IPageToolbarManager.cs
  29. 17
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbar.cs
  30. 27
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributionContext.cs
  31. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributor.cs
  32. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributorList.cs
  33. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarDictionary.cs
  34. 84
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarExtensions.cs
  35. 26
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarItem.cs
  36. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarItemList.cs
  37. 44
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarManager.cs
  38. 52
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/SimplePageToolbarContributor.cs
  39. 22
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/AbpPageToolbarViewComponent.cs
  40. 82
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs
  41. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml
  42. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Default.cshtml
  43. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/_ViewImports.cshtml
  44. 77
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
  45. 192
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js
  46. 21
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  47. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
  48. 19
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  49. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  50. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs
  51. 26
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs
  52. 48
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs
  53. 65
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs
  54. 37
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/ExtraPropertyBindingHelper.cs
  55. 12
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpModelMetadataProvider.cs
  56. 20
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs
  57. 132
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ViewFeatures/AbpValidationHtmlAttributeProvider.cs
  58. 98
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs
  59. 56
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Http/CliHttpClient.cs
  60. 39
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/AbpIoApiKeyService.cs
  61. 97
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs
  62. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs
  63. 62
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  64. 27
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs
  65. 2
      framework/src/Volo.Abp.Core/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs
  66. 18
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  67. 133
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  68. 9
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs
  69. 4
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/en.json
  70. 25
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/tr.json
  71. 0
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout.tpl
  72. 0
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message.tpl
  73. 14
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplateDefinitionProvider.cs
  74. 8
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  75. 5
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs
  76. 127
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModelingTypeHelper.cs
  77. 5
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs
  78. 12
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  79. 5
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs
  80. 3
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs
  81. 11
      framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs
  82. 10
      framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactoryWithDefaultResourceSupport.cs
  83. 36
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/HasNameWithLocalizableDisplayNameExtensions.cs
  84. 13
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IHasNameWithLocalizableDisplayName.cs
  85. 8
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs
  86. 47
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs
  87. 9
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableString.cs
  88. 5
      framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj
  89. 43
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
  90. 6
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs
  91. 12
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs
  92. 149
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs
  93. 62
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfiguration.cs
  94. 9
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfigurationDictionary.cs
  95. 23
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiConfiguration.cs
  96. 10
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiCreateConfiguration.cs
  97. 10
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiGetConfiguration.cs
  98. 10
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiUpdateConfiguration.cs
  99. 66
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs
  100. 9
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationDictionary.cs

17
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -13,8 +13,9 @@
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"Permission:Create": "Create",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Permission:Accounting:Invoice": "Invoice",
"Menu:Organizations": "Organizations",
"Menu:Accounting": "Accounting",
"Menu:Packages": "Packages",
@ -101,13 +102,10 @@
"CompanyName": "Company name",
"CompanyAddress": "Company address",
"Price": "Price",
<<<<<<< Updated upstream
"DiscountText": "Discount text",
"DiscountQuantity": "Discount quantity",
"DiscountPrice": "Discount price",
"DiscountPrice": "Discount price",
"Quotation": "Quotation",
"Generate": "Generate"
=======
"ExtraText": "Extra Text",
"ExtraAmount": "Extra Amount",
"DownloadQuotation": "Download Quotation",
@ -117,9 +115,10 @@
"InvoiceDate": "Invoice Date",
"Quantity": "Quantity",
"AddProduct": "Add Product",
"NeedToAddProduct": "Need to add product to generate invoice !",
"AddProductWarning": "You need to add product!",
"TotalPrice": "Total Price",
"Generate": "Generate"
>>>>>>> Stashed changes
"Generate": "Generate",
"MissingQuantityField": "The quantity field is required!",
"MissingPriceField": "The Price field is required!"
}
}

52
docs/en/Localization.md

@ -87,6 +87,21 @@ A JSON localization file content is shown below:
* Every localization file should define the `culture` code for the file (like "en" or "en-US").
* `texts` section just contains key-value collection of the localization strings (keys may have spaces too).
### Default Resource
`AbpLocalizationOptions.DefaultResourceType` can be set to a resource type, so it is used when the localization resource was not specified:
````csharp
Configure<AbpLocalizationOptions>(options =>
{
options.DefaultResourceType = typeof(TestResource);
});
````
> The [application startup template](Startup-Templates/Application.md) sets `DefaultResourceType` to the localization resource of the application.
See the *Client Side* section below for a use case.
### Short Localization Resource Name
Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example:
@ -166,6 +181,10 @@ public class MyService
}
````
##### Format Arguments
Format arguments can be passed after the localization key. If your message is `Hello {0}, welcome!`, then you can pass the `{0}` argument to the localizer like `_localizer["HelloMessage", "John"]`
#### Simplest Usage In A Razor View/Page
````c#
@ -180,18 +199,47 @@ Refer to the [Microsoft's localization documentation](https://docs.microsoft.com
ABP provides JavaScript services to use the same localized texts in the client side.
Get a localization resource:
#### getResource
`abp.localization.getResource` function is used to get a localization resource:
````js
var testResource = abp.localization.getResource('Test');
````
Localize a string:
Then you can localize a string based on this resource:
````js
var str = testResource('HelloWorld');
````
#### localize
`abp.localization.localize` function is a shortcut where you can both specify the text name and the resource name:
````js
var str = abp.localization.localize('HelloWorld', 'Test');
````
`HelloWorld` is the text to localize, where `Test` is the localization resource name here.
If you don't specify the localization resource name, it uses the default localization resource defined on the `AbpLocalizationOptions` (see the *Default Resource* section above). Example:
````js
var str = abp.localization.localize('HelloWorld'); //uses the default resource
````
##### Format Arguments
If your localized string contains arguments, like `Hello {0}, welcome!`, you can pass arguments to the localization methods. Examples:
````js
var str1 = abp.localization.getResource('Test')('HelloWelcomeMessage', 'John');
var str2 = abp.localization.localize('HelloWorld', 'Test', 'John');
````
Both of the samples above produce the output `Hello John, welcome!`.
## See Also
* [Localization in Angular UI](UI/Angular/Localization.md)

8
docs/en/Object-Extensions.md

@ -197,8 +197,8 @@ ObjectExtensionManager.Instance
"SocialSecurityNumber",
options =>
{
options.ValidationAttributes.Add(new RequiredAttribute());
options.ValidationAttributes.Add(
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
}
@ -248,12 +248,12 @@ ObjectExtensionManager.Instance
objConfig.AddOrUpdateProperty<string>("Password", propertyConfig =>
{
propertyConfig.ValidationAttributes.Add(new RequiredAttribute());
propertyConfig.Attributes.Add(new RequiredAttribute());
});
objConfig.AddOrUpdateProperty<string>("PasswordRepeat", propertyConfig =>
{
propertyConfig.ValidationAttributes.Add(new RequiredAttribute());
propertyConfig.Attributes.Add(new RequiredAttribute());
});
//Write a common validation logic works on multiple properties

11
framework/Volo.Abp.sln

@ -279,9 +279,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending",
EndProject
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}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Validation.Abstractions", "src\Volo.Abp.Validation.Abstractions\Volo.Abp.Validation.Abstractions.csproj", "{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -841,6 +843,10 @@ Global
{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
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -984,6 +990,7 @@ Global
{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}
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

3
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs

@ -1,4 +1,5 @@
using System;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
@ -19,5 +20,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
public MultiTenancyInfoDto MultiTenancy { get; set; }
public CurrentTenantDto CurrentTenant { get; set; }
public ObjectExtensionsDto ObjectExtensions { get; set; }
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs

@ -14,6 +14,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
public CurrentCultureDto CurrentCulture { get; set; }
public string DefaultResourceName { get; set; }
public ApplicationLocalizationConfigurationDto()
{
Values = new Dictionary<string, Dictionary<string, string>>();

178
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs

@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.ObjectExtending;
using Volo.Abp.ObjectExtending.Modularity;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
public class CachedObjectExtensionsDtoService : ICachedObjectExtensionsDtoService, ISingletonDependency
{
private volatile ObjectExtensionsDto _cachedValue;
private readonly object _syncLock = new object();
public virtual ObjectExtensionsDto Get()
{
if (_cachedValue == null)
{
lock (_syncLock)
{
if (_cachedValue == null)
{
_cachedValue = GenerateCacheValue();
}
}
}
return _cachedValue;
}
protected virtual ObjectExtensionsDto GenerateCacheValue()
{
var objectExtensionsDto = new ObjectExtensionsDto
{
Modules = new Dictionary<string, ModuleExtensionDto>()
};
foreach (var moduleConfig in ObjectExtensionManager.Instance.Modules())
{
objectExtensionsDto.Modules[moduleConfig.Key] = CreateModuleExtensionDto(moduleConfig.Value);
}
return objectExtensionsDto;
}
protected virtual ModuleExtensionDto CreateModuleExtensionDto(
ModuleExtensionConfiguration moduleConfig)
{
var moduleExtensionDto = new ModuleExtensionDto
{
Entities = new Dictionary<string, EntityExtensionDto>()
};
foreach (var objectConfig in moduleConfig.Entities)
{
moduleExtensionDto.Entities[objectConfig.Key] = GetEntityExtensionDto(objectConfig.Value);
}
foreach (var customConfig in moduleConfig.Configuration.Where(c => !c.Key.StartsWith("_")))
{
moduleExtensionDto.Configuration[customConfig.Key] = customConfig.Value;
}
return moduleExtensionDto;
}
protected virtual EntityExtensionDto GetEntityExtensionDto(
EntityExtensionConfiguration entityConfig)
{
var entityExtensionDto = new EntityExtensionDto
{
Properties = new Dictionary<string, ExtensionPropertyDto>(),
Configuration = new Dictionary<string, object>()
};
foreach (var propertyConfig in entityConfig.GetProperties())
{
if (!propertyConfig.IsAvailableToClients)
{
continue;
}
entityExtensionDto.Properties[propertyConfig.Name] = CreateExtensionPropertyDto(propertyConfig);
}
foreach (var customConfig in entityConfig.Configuration.Where(c => !c.Key.StartsWith("_")))
{
entityExtensionDto.Configuration[customConfig.Key] = customConfig.Value;
}
return entityExtensionDto;
}
protected virtual ExtensionPropertyDto CreateExtensionPropertyDto(
ExtensionPropertyConfiguration propertyConfig)
{
var extensionPropertyDto = new ExtensionPropertyDto
{
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(propertyConfig.Type),
TypeSimple = TypeHelper.GetSimplifiedName(propertyConfig.Type),
Attributes = new List<ExtensionPropertyAttributeDto>(),
DisplayName = CreateDisplayNameDto(propertyConfig),
Configuration = new Dictionary<string, object>(),
Api = new ExtensionPropertyApiDto
{
OnGet = new ExtensionPropertyApiGetDto
{
IsAvailable = propertyConfig.Api.OnGet.IsAvailable
},
OnCreate = new ExtensionPropertyApiCreateDto
{
IsAvailable = propertyConfig.Api.OnCreate.IsAvailable
},
OnUpdate = new ExtensionPropertyApiUpdateDto
{
IsAvailable = propertyConfig.Api.OnUpdate.IsAvailable
}
},
Ui = new ExtensionPropertyUiDto
{
OnCreateForm = new ExtensionPropertyUiFormDto
{
IsVisible = propertyConfig.UI.OnCreateForm.IsVisible
},
OnEditForm = new ExtensionPropertyUiFormDto
{
IsVisible = propertyConfig.UI.OnEditForm.IsVisible
},
OnTable = new ExtensionPropertyUiTableDto
{
IsVisible = propertyConfig.UI.OnTable.IsVisible
}
}
};
foreach (var attribute in propertyConfig.Attributes)
{
extensionPropertyDto.Attributes.Add(
ExtensionPropertyAttributeDto.Create(attribute)
);
}
foreach (var customConfig in propertyConfig.Configuration.Where(c => !c.Key.StartsWith("_")))
{
extensionPropertyDto.Configuration[customConfig.Key] = customConfig.Value;
}
return extensionPropertyDto;
}
protected virtual LocalizableStringDto CreateDisplayNameDto(ExtensionPropertyConfiguration propertyConfig)
{
if (propertyConfig.DisplayName == null)
{
return null;
}
if (propertyConfig.DisplayName is LocalizableString localizableStringInstance)
{
return new LocalizableStringDto(
localizableStringInstance.Name,
localizableStringInstance.ResourceType != null
? LocalizationResourceNameAttribute.GetName(localizableStringInstance.ResourceType)
: null
);
}
if (propertyConfig.DisplayName is FixedLocalizableString fixedLocalizableString)
{
// "_" means don't use the default resource, but directly use the name.
return new LocalizableStringDto(fixedLocalizableString.Value, "_");
}
return null;
}
}
}

14
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class EntityExtensionDto
{
public Dictionary<string, ExtensionPropertyDto> Properties { get; set; }
public Dictionary<string, object> Configuration { get; set; }
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiCreateDto.cs

@ -0,0 +1,13 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyApiCreateDto
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

23
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiDto.cs

@ -0,0 +1,23 @@
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
public class ExtensionPropertyApiDto
{
[NotNull]
public ExtensionPropertyApiGetDto OnGet { get; set; }
[NotNull]
public ExtensionPropertyApiCreateDto OnCreate { get; set; }
[NotNull]
public ExtensionPropertyApiUpdateDto OnUpdate { get; set; }
public ExtensionPropertyApiDto()
{
OnGet = new ExtensionPropertyApiGetDto();
OnCreate = new ExtensionPropertyApiCreateDto();
OnUpdate = new ExtensionPropertyApiUpdateDto();
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiGetDto.cs

@ -0,0 +1,13 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyApiGetDto
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyApiUpdateDto.cs

@ -0,0 +1,13 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyApiUpdateDto
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

36
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyAttributeDto.cs

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyAttributeDto
{
public string Type { get; set; }
public string TypeSimple { get; set; }
public Dictionary<string, object> Configuration { get; set; }
public static ExtensionPropertyAttributeDto Create(Attribute attribute)
{
var attributeType = attribute.GetType();
var dto = new ExtensionPropertyAttributeDto
{
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(attributeType),
TypeSimple = TypeHelper.GetSimplifiedName(attributeType),
Configuration = new Dictionary<string, object>()
};
if (attribute is StringLengthAttribute stringLengthAttribute)
{
dto.Configuration["MaximumLength"] = stringLengthAttribute.MaximumLength;
dto.Configuration["MinimumLength"] = stringLengthAttribute.MinimumLength;
}
//TODO: Others!
return dto;
}
}
}

25
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyDto
{
public string Type { get; set; }
public string TypeSimple { get; set; }
[CanBeNull]
public LocalizableStringDto DisplayName { get; set; }
public ExtensionPropertyApiDto Api { get; set; }
public ExtensionPropertyUiDto Ui { get; set; }
public List<ExtensionPropertyAttributeDto> Attributes { get; set; }
public Dictionary<string, object> Configuration { get; set; }
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiDto.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyUiDto
{
public ExtensionPropertyUiTableDto OnTable { get; set; }
public ExtensionPropertyUiFormDto OnCreateForm { get; set; }
public ExtensionPropertyUiFormDto OnEditForm { get; set; }
}
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiFormDto.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyUiFormDto
{
public bool IsVisible { get; set; }
}
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyUiTableDto.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionPropertyUiTableDto
{
public bool IsVisible { get; set; }
}
}

7
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ICachedObjectExtensionsDtoService.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
public interface ICachedObjectExtensionsDtoService
{
ObjectExtensionsDto Get();
}
}

21
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/LocalizableStringDto.cs

@ -0,0 +1,21 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class LocalizableStringDto
{
[NotNull]
public string Name { get; private set; }
[CanBeNull]
public string Resource { get; set; }
public LocalizableStringDto([NotNull] string name, string resource = null)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
Resource = resource;
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionConfigurationDto.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ModuleExtensionDto
{
public Dictionary<string, EntityExtensionDto> Entities { get; set; }
public Dictionary<string, object> Configuration { get; set; }
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ObjectExtensionsDto
{
public Dictionary<string, ModuleExtensionDto> Modules { get; set; }
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs

@ -144,8 +144,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
}
}
}
protected virtual void RemoveFormGroupItemsNotInModel(TagHelperContext context, TagHelperOutput output, List<FormGroupItem> items)
{
var models = GetModels(context, output);

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs

@ -21,6 +21,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public bool AutoFocus { get; set; }
[HtmlAttributeName("type")]
public string InputTypeName { get; set; }
public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default;
[HtmlAttributeName("required-symbol")]

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -117,6 +117,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
return new InputTagHelper(_generator)
{
For = TagHelper.AspFor,
InputTypeName = TagHelper.InputTypeName,
ViewContext = TagHelper.ViewContext
};
}
@ -331,6 +332,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
var groupPrefix = "group-";
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList();
var attrList = new TagHelperAttributeList();
foreach (var tagHelperAttribute in tagHelperAttributes)
@ -338,6 +340,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
attrList.Add(tagHelperAttribute);
}
if (!TagHelper.InputTypeName.IsNullOrEmpty() && !attrList.ContainsName("type"))
{
attrList.Add("type", TagHelper.InputTypeName);
}
return attrList;
}

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -36,6 +36,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
{
context.Files.AddRange(new[]
{
"/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js",

31
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/AbpPageToolbarOptions.cs

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class AbpPageToolbarOptions
{
public PageToolbarDictionary Toolbars { get; }
public AbpPageToolbarOptions()
{
Toolbars = new PageToolbarDictionary();
}
public void Configure<TPage>([NotNull] Action<PageToolbar> configureAction)
{
// ReSharper disable once AssignNullToNotNullAttribute
Configure(typeof(TPage).FullName, configureAction);
}
public void Configure([NotNull]string pageName, [NotNull]Action<PageToolbar> configureAction)
{
Check.NotNullOrWhiteSpace(pageName, nameof(pageName));
Check.NotNull(configureAction, nameof(configureAction));
var toolbar = Toolbars.GetOrAdd(pageName, () => new PageToolbar(pageName));
configureAction(toolbar);
}
}
}

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/IPageToolbarContributor.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public interface IPageToolbarContributor
{
Task ContributeAsync(PageToolbarContributionContext context);
}
}

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/IPageToolbarManager.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public interface IPageToolbarManager
{
Task<PageToolbarItem[]> GetItemsAsync(string pageName);
}
}

17
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbar.cs

@ -0,0 +1,17 @@
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbar
{
public string PageName { get; }
public PageToolbarContributorList Contributors { get; set; }
public PageToolbar([NotNull] string pageName)
{
PageName = Check.NotNullOrEmpty(pageName, nameof(pageName));
Contributors = new PageToolbarContributorList();
}
}
}

27
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributionContext.cs

@ -0,0 +1,27 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarContributionContext
{
[NotNull]
public string PageName { get; }
[NotNull]
public IServiceProvider ServiceProvider { get; }
[NotNull]
public PageToolbarItemList Items { get; }
public PageToolbarContributionContext(
[NotNull] string pageName,
[NotNull] IServiceProvider serviceProvider)
{
PageName = Check.NotNull(pageName, nameof(pageName));
ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider));
Items = new PageToolbarItemList();
}
}
}

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributor.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public abstract class PageToolbarContributor : IPageToolbarContributor
{
public abstract Task ContributeAsync(PageToolbarContributionContext context);
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarContributorList.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarContributorList : List<IPageToolbarContributor>
{
}
}

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarDictionary.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarDictionary : Dictionary<string, PageToolbar>
{
}
}

84
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarExtensions.cs

@ -0,0 +1,84 @@
using System;
using Localization.Resources.AbpUi;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar.Button;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public static class PageToolbarExtensions
{
public static PageToolbar AddComponent<TComponent>(
this PageToolbar toolbar,
object argument = null,
int order = 0,
string requiredPolicyName = null)
{
return toolbar.AddComponent(
typeof(TComponent),
argument,
order,
requiredPolicyName
);
}
public static PageToolbar AddComponent(
this PageToolbar toolbar,
Type componentType,
object argument = null,
int order = 0,
string requiredPolicyName = null)
{
toolbar.Contributors.Add(
new SimplePageToolbarContributor(
componentType,
argument,
order,
requiredPolicyName
)
);
return toolbar;
}
public static PageToolbar AddButton(
this PageToolbar toolbar,
ILocalizableString text,
string icon = null,
string name = null,
string id = null,
ILocalizableString busyText = null,
FontIconType iconType = FontIconType.FontAwesome,
AbpButtonType type = AbpButtonType.Primary,
AbpButtonSize size = AbpButtonSize.Small,
bool disabled = false,
int order = 0,
string requiredPolicyName = null)
{
if (busyText == null)
{
busyText = new LocalizableString(typeof(AbpUiResource), "ProcessingWithThreeDot");
}
toolbar.AddComponent<AbpPageToolbarButtonViewComponent>(
new
{
text,
icon,
name,
id,
busyText,
iconType,
type,
size,
disabled
},
order,
requiredPolicyName
);
return toolbar;
}
}
}

26
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarItem.cs

@ -0,0 +1,26 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarItem
{
[NotNull]
public Type ComponentType { get; }
[CanBeNull]
public object Arguments { get; set; }
public int Order { get; set; }
public PageToolbarItem(
[NotNull] Type componentType,
[CanBeNull] object arguments = null,
int order = 0)
{
ComponentType = Check.NotNull(componentType, nameof(componentType));
Arguments = arguments;
Order = order;
}
}
}

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarItemList.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarItemList : List<PageToolbarItem>
{
}
}

44
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/PageToolbarManager.cs

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbarManager : IPageToolbarManager, ITransientDependency
{
protected AbpPageToolbarOptions Options { get; }
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
public PageToolbarManager(
IOptions<AbpPageToolbarOptions> options,
IHybridServiceScopeFactory serviceScopeFactory)
{
Options = options.Value;
ServiceScopeFactory = serviceScopeFactory;
}
public virtual async Task<PageToolbarItem[]> GetItemsAsync(string pageName)
{
var toolbar = Options.Toolbars.GetOrDefault(pageName);
if (toolbar == null || !toolbar.Contributors.Any())
{
return Array.Empty<PageToolbarItem>();
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var context = new PageToolbarContributionContext(pageName, scope.ServiceProvider);
foreach (var contributor in toolbar.Contributors)
{
await contributor.ContributeAsync(context);
}
return context.Items.OrderBy(i => i.Order).ToArray();
}
}
}
}

52
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/PageToolbars/SimplePageToolbarContributor.cs

@ -0,0 +1,52 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class SimplePageToolbarContributor : IPageToolbarContributor
{
public Type ComponentType { get; }
public object Argument { get; set; }
public int Order { get; }
public string RequiredPolicyName { get; }
public SimplePageToolbarContributor(
Type componentType,
object argument = null,
int order = 0,
string requiredPolicyName = null)
{
ComponentType = componentType;
Argument = argument;
Order = order;
RequiredPolicyName = requiredPolicyName;
}
public async Task ContributeAsync(PageToolbarContributionContext context)
{
if(await ShouldAddComponentAsync(context))
{
context.Items.Add(new PageToolbarItem(ComponentType, Argument, Order));
}
}
protected virtual async Task<bool> ShouldAddComponentAsync(PageToolbarContributionContext context)
{
if (RequiredPolicyName != null)
{
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>();
if (!await authorizationService.IsGrantedAsync(RequiredPolicyName))
{
return false;
}
}
return true;
}
}
}

22
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/AbpPageToolbarViewComponent.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar
{
public class AbpPageToolbarViewComponent : AbpViewComponent
{
private readonly IPageToolbarManager _toolbarManager;
public AbpPageToolbarViewComponent(IPageToolbarManager toolbarManager)
{
_toolbarManager = toolbarManager;
}
public async Task<IViewComponentResult> InvokeAsync(string pageName)
{
var items = await _toolbarManager.GetItemsAsync(pageName);
return View("~/Pages/Shared/Components/AbpPageToolbar/Default.cshtml", items);
}
}
}

82
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs

@ -0,0 +1,82 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar.Button
{
public class AbpPageToolbarButtonViewComponent : AbpViewComponent
{
protected IStringLocalizerFactory StringLocalizerFactory { get; }
public AbpPageToolbarButtonViewComponent(IStringLocalizerFactory stringLocalizerFactory)
{
StringLocalizerFactory = stringLocalizerFactory;
}
public IViewComponentResult Invoke(
ILocalizableString text,
string name,
string icon,
string id,
ILocalizableString busyText,
FontIconType iconType,
AbpButtonType type,
AbpButtonSize size,
bool disabled)
{
Check.NotNull(text, nameof(text));
return View(
"~/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml",
new AbpPageToolbarButtonViewModel(
text.Localize(StringLocalizerFactory),
name,
icon,
id,
busyText?.Localize(StringLocalizerFactory),
iconType,
type,
size,
disabled
)
);
}
public class AbpPageToolbarButtonViewModel
{
public string Text { get; }
public string Name { get; }
public string Icon { get; }
public string Id { get; }
public string BusyText { get; }
public FontIconType IconType { get; }
public AbpButtonType Type { get; }
public AbpButtonSize Size { get; }
public bool Disabled { get; }
public AbpPageToolbarButtonViewModel(
string text,
string name,
string icon,
string id,
string busyText,
FontIconType iconType,
AbpButtonType type,
AbpButtonSize size,
bool disabled)
{
Text = text;
Name = name;
Icon = icon;
Id = id;
BusyText = busyText;
IconType = iconType;
Type = type;
Size = size;
Disabled = disabled;
}
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml

@ -0,0 +1,13 @@
@model Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar.Button.AbpPageToolbarButtonViewComponent.AbpPageToolbarButtonViewModel
<abp-button
text="@Model.Text"
name="@Model.Name"
icon="@Model.Icon"
id="@Model.Id"
busy-text="@Model.BusyText"
icon-type="@Model.IconType"
button-type="@Model.Type"
size="@Model.Size"
disabled="@Model.Disabled"
class="mx-1"
/>

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Default.cshtml

@ -0,0 +1,5 @@
@model Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars.PageToolbarItem[]
@foreach (var toolbarItem in Model)
{
@(await Component.InvokeAsync(toolbarItem.ComponentType, toolbarItem.Arguments))
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/_ViewImports.cshtml

@ -0,0 +1,4 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling

77
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js

@ -1,60 +1,16 @@
var abp = abp;
var abp = abp || {};
(function ($) {
/************************************************************************
* RECORD-ACTIONS extension for datatables
---------------------------------------------------------------
* SINGLE BUTTON USAGE (creates the given JQuery element)
{
targets: 0, //optional
rowAction:
{
element: $("<button/>")
.addClass("btn btn-primary btn-sm m-btn--icon")
.text("My button")
.prepend($("<i/>").addClass("la la-sign-in"))
.click(function () {
console.log($(this).data());
})
},
},
---------------------------------------------------------------
* LIST OF ITEMS USAGE
{
targets: 0, //optional
rowAction:
{
text: 'My actions', //optional. default value: Actions
icon: 'bolt' //optional. default value: cog. See fa icon set https://fontawesome.com/v4.7.0/icons/
items:
[
{
text: "My first action", //mandatory
icon: "thumbs-o-down", //optional.
visible: true //optional. default value: true. Accepts boolean returning function too. Eg: function(){ return true/false;} ,
action: function (data) {
console.log(data.record);
}
},
{
text: "My second action",
icon: "thumbs-o-up",
visible: true,
action: function (data) {
console.log(data.record);
}
}
]
}
},
*************************************************************************/
var datatables = abp.utils.createNamespace(abp, 'libs.datatables');
var localize = function (key) {
return abp.localization.getResource('AbpUi')(key);
};
var recordActions = function () {
/************************************************************************
* RECORD-ACTIONS extension for datatables *
*************************************************************************/
(function () {
if (!$.fn.dataTableExt) {
return;
}
@ -117,7 +73,7 @@
.addClass('action-button');
var $dropdownButton = $('<button/>');
if (field.icon !== undefined) {
if (field.icon) {
$dropdownButton.append($("<i>").addClass("fa fa-" + field.icon + " mr-1"));
@ -267,14 +223,12 @@
}
});
}();
})();
/************************************************************************
* AJAX extension for datatables *
*************************************************************************/
var datatables = abp.utils.createNamespace(abp, 'libs.datatables');
var ajaxActions = function () {
(function () {
datatables.createAjax = function (serverMethod, inputAction) {
return function (requestData, callback, settings) {
var input = inputAction ? inputAction() : {};
@ -318,14 +272,14 @@
}
};
};
}();
})();
/************************************************************************
* Configuration/Options normalizer for datatables *
*************************************************************************/
var optionNormalizer = function () {
(function () {
var customizeRowActionColumn = function(column) {
var customizeRowActionColumn = function (column) {
column.data = null;
column.orderable = false;
column.defaultContent = "";
@ -336,6 +290,9 @@
};
datatables.normalizeConfiguration = function (configuration) {
configuration.scrollX = true;
for (var i = 0; i < configuration.columnDefs.length; i++) {
var column = configuration.columnDefs[i];
if (!column.targets) {
@ -369,6 +326,6 @@
return configuration;
};
}();
})();
})(jQuery);

192
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js

@ -0,0 +1,192 @@
var abp = abp || {};
(function () {
abp.ui = abp.ui || {};
abp.ui.extensions = abp.ui.extensions || {};
abp.ui.extensions.ActionList = function () {
return new abp.utils.common.LinkedList();
};
abp.ui.extensions.ColumnList = function () {
return new abp.utils.common.LinkedList();
};
abp.ui.extensions.entityActions = (function () {
var _callbackLists = {};
function _get(name) {
var callbackList = _callbackLists[name];
if (!callbackList) {
callbackList = _callbackLists[name] = [];
}
return {
addContributor: _addContributor,
get actions() {
return _getActions();
}
};
function _addContributor(contributeCallback, order) {
if (order === undefined || order >= callbackList.length) {
callbackList.push(contributeCallback);
} else if (order === 0) {
callbackList.unshift(contributeCallback);
} else {
callbackList.splice(order, 0, contributeCallback);
}
}
function _getActions() {
var actionList = new abp.ui.extensions.ActionList();
callbackList.forEach(function (callback) {
callback(actionList);
});
return actionList;
}
}
return {
get: _get
};
})();
abp.ui.extensions.tableColumns = (function () {
var _callbackLists = {};
function _get(name) {
var callbackList = _callbackLists[name];
if (!callbackList) {
callbackList = _callbackLists[name] = [];
}
return {
addContributor: _addContributor,
get columns() {
return _getColumns();
}
};
function _addContributor(contributeCallback, order) {
if (order === undefined || order >= callbackList.length) {
callbackList.push(contributeCallback);
} else if (order === 0) {
callbackList.unshift(contributeCallback);
} else {
callbackList.splice(order, 0, contributeCallback);
}
}
function _getColumns() {
var columnList = new abp.ui.extensions.ColumnList();
callbackList.forEach(function (callback) {
callback(columnList);
});
return columnList;
}
}
return {
get: _get
};
})();
function initializeObjectExtensions() {
function localizeDisplayName(propertyName, displayName) {
if (displayName && displayName.name) {
return abp.localization.localize(displayName.name, displayName.resource);
}
if (abp.localization.isLocalized('DisplayName:' + propertyName)) {
return abp.localization.localize('DisplayName:' + propertyName);
}
return abp.localization.localize(propertyName);
}
function configureTableColumns(tableName, columnConfigs) {
abp.ui.extensions.tableColumns.get(tableName)
.addContributor(
function (columnList) {
columnList.addManyTail(columnConfigs);
}
);
}
function getTableProperties(objectConfig) {
var propertyNames = Object.keys(objectConfig.properties);
var tableProperties = [];
for (var i = 0; i < propertyNames.length; i++) {
var propertyName = propertyNames[i];
var propertyConfig = objectConfig.properties[propertyName];
if (propertyConfig.ui.onTable.isVisible) {
tableProperties.push({
name: propertyName,
config: propertyConfig
});
}
}
return tableProperties;
}
function convertPropertiesToColumnConfigs(properties) {
var columnConfigs = [];
for (var i = 0; i < properties.length; i++) {
var tableProperty = properties[i];
columnConfigs.push({
title: localizeDisplayName(tableProperty.name, tableProperty.config.displayName),
data: "extraProperties." + tableProperty.name
});
}
return columnConfigs;
}
function configureEntity(moduleName, entityName, entityConfig) {
var tableProperties = getTableProperties(entityConfig);
if (tableProperties.length > 0) {
var tableName = abp.utils.toCamelCase(moduleName) + "." + abp.utils.toCamelCase(entityName);
var columnConfigs = convertPropertiesToColumnConfigs(tableProperties);
configureTableColumns(
tableName,
columnConfigs
);
}
}
function configureModule(moduleName, moduleConfig) {
var entityNames = Object.keys(moduleConfig.entities);
for (var i = 0; i < entityNames.length; i++) {
configureEntity(
moduleName,
entityNames[i],
moduleConfig.entities[entityNames[i]]
);
}
}
var moduleNames = Object.keys(abp.objectExtensions.modules);
for (var i = 0; i < moduleNames.length; i++) {
configureModule(
moduleNames[i],
abp.objectExtensions.modules[moduleNames[i]]
);
}
}
abp.event.on('abp.configurationInitialized', function () {
initializeObjectExtensions();
});
})();

21
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Volo.Abp.ApiVersioning;
using Volo.Abp.AspNetCore.Mvc.ApiExploring;
using Volo.Abp.AspNetCore.Mvc.Conventions;
@ -90,8 +91,11 @@ namespace Volo.Abp.AspNetCore.Mvc
var mvcCoreBuilder = context.Services.AddMvcCore();
context.Services.ExecutePreConfiguredActions(mvcCoreBuilder);
var abpMvcDataAnnotationsLocalizationOptions = context.Services.ExecutePreConfiguredActions(new AbpMvcDataAnnotationsLocalizationOptions());
var abpMvcDataAnnotationsLocalizationOptions = context.Services
.ExecutePreConfiguredActions(
new AbpMvcDataAnnotationsLocalizationOptions()
);
context.Services
.AddSingleton<IOptions<AbpMvcDataAnnotationsLocalizationOptions>>(
@ -111,8 +115,17 @@ namespace Volo.Abp.AspNetCore.Mvc
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var resourceType = abpMvcDataAnnotationsLocalizationOptions.AssemblyResources.GetOrDefault(type.Assembly);
return factory.Create(resourceType ?? type);
var resourceType = abpMvcDataAnnotationsLocalizationOptions
.AssemblyResources
.GetOrDefault(type.Assembly);
if (resourceType != null)
{
return factory.Create(resourceType);
}
return factory.CreateDefaultOrNull() ??
factory.Create(type);
};
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs

@ -5,6 +5,7 @@ using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Features;
using Volo.Abp.AspNetCore.Mvc.ModelBinding;
using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata;
using Volo.Abp.AspNetCore.Mvc.Response;
using Volo.Abp.AspNetCore.Mvc.Uow;
using Volo.Abp.AspNetCore.Mvc.Validation;
@ -48,6 +49,7 @@ namespace Volo.Abp.AspNetCore.Mvc
private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
options.ModelBinderProviders.Insert(0, new AbpExtraPropertiesDictionaryModelBinderProvider());
}
private static void AddMetadataProviders(MvcOptions options, IServiceCollection services)

19
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -8,6 +8,7 @@ using System.Globalization;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Application.Services;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending;
using Volo.Abp.AspNetCore.Mvc.MultiTenancy;
using Volo.Abp.Authorization;
using Volo.Abp.Features;
@ -30,6 +31,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
private readonly ISettingDefinitionManager _settingDefinitionManager;
private readonly IFeatureDefinitionManager _featureDefinitionManager;
private readonly ILanguageProvider _languageProvider;
private readonly ICachedObjectExtensionsDtoService _cachedObjectExtensionsDtoService;
public AbpApplicationConfigurationAppService(
IOptions<AbpLocalizationOptions> localizationOptions,
@ -41,7 +43,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
ISettingProvider settingProvider,
ISettingDefinitionManager settingDefinitionManager,
IFeatureDefinitionManager featureDefinitionManager,
ILanguageProvider languageProvider)
ILanguageProvider languageProvider,
ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService)
{
_serviceProvider = serviceProvider;
_abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider;
@ -51,6 +54,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
_settingDefinitionManager = settingDefinitionManager;
_featureDefinitionManager = featureDefinitionManager;
_languageProvider = languageProvider;
_cachedObjectExtensionsDtoService = cachedObjectExtensionsDtoService;
_localizationOptions = localizationOptions.Value;
_multiTenancyOptions = multiTenancyOptions.Value;
}
@ -67,12 +71,12 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
CurrentUser = GetCurrentUser(),
Setting = await GetSettingConfigAsync(),
MultiTenancy = GetMultiTenancy(),
CurrentTenant = GetCurrentTenant()
CurrentTenant = GetCurrentTenant(),
ObjectExtensions = _cachedObjectExtensionsDtoService.Get()
};
}
protected virtual CurrentTenantDto GetCurrentTenant()
protected virtual CurrentTenantDto GetCurrentTenant()
{
return new CurrentTenantDto()
{
@ -154,6 +158,13 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
localizationConfig.CurrentCulture = GetCurrentCultureInfo();
if (_localizationOptions.DefaultResourceType != null)
{
localizationConfig.DefaultResourceName = LocalizationResourceNameAttribute.GetName(
_localizationOptions.DefaultResourceType
);
}
Logger.LogDebug("Executed AbpApplicationConfigurationAppService.GetLocalizationConfigAsync()");
return localizationConfig;

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -206,7 +206,7 @@ namespace Volo.Abp.AspNetCore.Mvc
/* TODO: Add interfaces
*/
var typeName = ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(type);
var typeName = TypeHelper.GetFullNameHandlingNullableAndGenerics(type);
if (applicationModel.Types.ContainsKey(typeName))
{

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs

@ -2,7 +2,7 @@
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using System;
using Volo.Abp.Settings;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.Localization
{
@ -13,7 +13,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization
[HttpGet]
public IActionResult Switch(string culture, string uiCulture = "", string returnUrl = "")
{
if (!GlobalizationHelper.IsValidCultureCode(culture))
if (!CultureHelper.IsValidCultureCode(culture))
{
throw new AbpException("Unknown language: " + culture + ". It must be a valid culture!");
}

26
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs

@ -1,26 +0,0 @@
using System;
using System.Globalization;
namespace Volo.Abp.AspNetCore.Mvc.Localization
{
internal static class GlobalizationHelper
{
public static bool IsValidCultureCode(string cultureCode)
{
if (cultureCode.IsNullOrWhiteSpace())
{
return false;
}
try
{
CultureInfo.GetCultureInfo(cultureCode);
return true;
}
catch (CultureNotFoundException)
{
return false;
}
}
}
}

48
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
public class AbpExtraPropertiesDictionaryModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType != typeof(Dictionary<string, object>))
{
return null;
}
if (!context.Metadata.ContainerType.IsAssignableTo<IHasExtraProperties>())
{
return null;
}
var binderType = typeof(DictionaryModelBinder<string, object>);
var keyBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(typeof(string)));
var valueBinder = new AbpExtraPropertyModelBinder(context.Metadata.ContainerType);
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
var mvcOptions = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
return (IModelBinder)Activator.CreateInstance(
binderType,
keyBinder,
valueBinder,
loggerFactory,
true /* allowValidatingTopLevelNodes */,
mvcOptions);
}
}
}

65
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs

@ -0,0 +1,65 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
public class AbpExtraPropertyModelBinder : IModelBinder
{
public Type ExtensibleObjectType { get; }
public AbpExtraPropertyModelBinder(Type extensibleObjectType)
{
ExtensibleObjectType = extensibleObjectType;
}
public virtual Task BindModelAsync(ModelBindingContext bindingContext)
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var model = ConvertStringToPropertyType(
bindingContext,
valueProviderResult.FirstValue
);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
protected virtual object ConvertStringToPropertyType(ModelBindingContext bindingContext, string value)
{
if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && string.IsNullOrWhiteSpace(value))
{
return null;
}
var extensionInfo = ObjectExtensionManager.Instance.GetOrNull(ExtensibleObjectType);
if (extensionInfo == null)
{
return value;
}
var propertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(bindingContext.ModelName);
if (propertyName == null)
{
return value;
}
var propertyInfo = extensionInfo.GetPropertyOrNull(propertyName);
if (propertyInfo == null)
{
return value;
}
return Convert.ChangeType(value, propertyInfo.Type);
}
}
}

37
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/ExtraPropertyBindingHelper.cs

@ -0,0 +1,37 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
{
public static class ExtraPropertyBindingHelper
{
/// <summary>
/// <paramref name="expression"/> is a string like "UserInfo.ExtraProperties[SocialSecurityNumber]"
/// This method returns "SocialSecurityNumber" for this example. */
/// </summary>
public static string ExtractExtraPropertyName(string expression)
{
var index = expression.IndexOf("ExtraProperties[", StringComparison.Ordinal);
if (index < 0)
{
return null;
}
return expression.Substring(index + 16, expression.Length - index - 17);
}
/// <summary>
/// <paramref name="expression"/> is a string like "UserInfo.ExtraProperties[SocialSecurityNumber]"
/// This method returns "UserInfo" for this example.
/// </summary>
public static string ExtractContainerName(string expression)
{
var index = expression.IndexOf("ExtraProperties[", StringComparison.Ordinal);
if (index < 0)
{
return null;
}
return expression.Left(index).TrimEnd('.');
}
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpModelMetadataProvider.cs

@ -1,11 +1,11 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata
@ -14,14 +14,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata
[ExposeServices(typeof(IModelMetadataProvider))]
public class AbpModelMetadataProvider : DefaultModelMetadataProvider
{
private static readonly PropertyInfo ValidationAttributeErrorMessageStringProperty;
static AbpModelMetadataProvider()
{
ValidationAttributeErrorMessageStringProperty = typeof(ValidationAttribute)
.GetProperty("ErrorMessageString", BindingFlags.Instance | BindingFlags.NonPublic);
}
public AbpModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider)
: base(detailsProvider)
{
@ -56,7 +48,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata
{
if (validationAttribute.ErrorMessage == null)
{
validationAttribute.ErrorMessage = ValidationAttributeErrorMessageStringProperty.GetValue(validationAttribute) as string;
ValidationAttributeHelper.SetDefaultErrorMessage(validationAttribute);
}
}
}

20
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text;
namespace Volo.Abp.AspNetCore.Mvc.Validation
{
public static class ValidationAttributeHelper
{
private static readonly PropertyInfo ValidationAttributeErrorMessageStringProperty = typeof(ValidationAttribute)
.GetProperty("ErrorMessageString", BindingFlags.Instance | BindingFlags.NonPublic);
public static void SetDefaultErrorMessage(ValidationAttribute validationAttribute)
{
validationAttribute.ErrorMessage =
ValidationAttributeErrorMessageStringProperty.GetValue(validationAttribute) as string;
}
}
}

132
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ViewFeatures/AbpValidationHtmlAttributeProvider.cs

@ -0,0 +1,132 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.ModelBinding;
using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Validation.Localization;
namespace Volo.Abp.AspNetCore.Mvc.ViewFeatures
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ValidationHtmlAttributeProvider))]
public class AbpValidationHtmlAttributeProvider
: DefaultValidationHtmlAttributeProvider, ISingletonDependency
{
private readonly IModelMetadataProvider _metadataProvider;
private readonly IStringLocalizerFactory _stringLocalizerFactory;
private readonly IStringLocalizer<AbpValidationResource> _validationStringLocalizer;
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
public AbpValidationHtmlAttributeProvider(
IOptions<MvcViewOptions> optionsAccessor,
IModelMetadataProvider metadataProvider,
ClientValidatorCache clientValidatorCache,
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
IStringLocalizerFactory stringLocalizerFactory,
IStringLocalizer<AbpValidationResource> validationStringLocalizer)
: base(
optionsAccessor,
metadataProvider,
clientValidatorCache)
{
_metadataProvider = metadataProvider;
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
_stringLocalizerFactory = stringLocalizerFactory;
_validationStringLocalizer = validationStringLocalizer;
}
public override void AddValidationAttributes(
ViewContext viewContext,
ModelExplorer modelExplorer,
IDictionary<string, string> attributes)
{
base.AddValidationAttributes(viewContext, modelExplorer, attributes);
AddExtraPropertyValidationsAttributes(viewContext, modelExplorer, attributes);
}
protected virtual void AddExtraPropertyValidationsAttributes(ViewContext viewContext, ModelExplorer modelExplorer, IDictionary<string, string> attributes)
{
var nameAttribute = attributes.GetOrDefault("name");
if (nameAttribute == null)
{
return;
}
var extraPropertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(nameAttribute);
if (extraPropertyName == null)
{
return;
}
//TODO: containerName can be null on controller actions..?
var containerName = ExtraPropertyBindingHelper.ExtractContainerName(nameAttribute);
if (containerName == null)
{
return;
}
if (modelExplorer.Container?.ModelType == null)
{
return;
}
var extensibleObjectType = modelExplorer.Container.ModelType
.GetProperty(containerName, BindingFlags.Instance | BindingFlags.Public)
?.PropertyType;
if (extensibleObjectType == null)
{
return;
}
var extensionPropertyInfo = ObjectExtensionManager.Instance.GetPropertyOrNull(
extensibleObjectType,
extraPropertyName
);
if (extensionPropertyInfo == null)
{
return;
}
if (modelExplorer.Metadata is DefaultModelMetadata metadata)
{
metadata.DisplayMetadata.DisplayName =
() => extensionPropertyInfo.GetLocalizedDisplayName(_stringLocalizerFactory);
}
foreach (var validationAttribute in extensionPropertyInfo.GetValidationAttributes())
{
var validationContext = new ClientModelValidationContext(
viewContext,
modelExplorer.Metadata,
_metadataProvider,
attributes
);
if (validationAttribute.ErrorMessage == null)
{
ValidationAttributeHelper.SetDefaultErrorMessage(validationAttribute);
}
var validationAttributeAdapter = _validationAttributeAdapterProvider.GetAttributeAdapter(
validationAttribute,
_validationStringLocalizer
);
validationAttributeAdapter?.AddValidation(validationContext);
}
}
}
}

98
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs

@ -0,0 +1,98 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Abp.ObjectExtending
{
public static class ObjectExtensionPropertyInfoAspNetCoreMvcExtensions
{
public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo)
{
foreach (var attribute in propertyInfo.Attributes)
{
var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute);
if (inputTypeByAttribute != null)
{
return inputTypeByAttribute;
}
}
return GetInputTypeFromTypeOrNull(propertyInfo.Type)
?? "text"; //default
}
private static string GetInputTypeFromAttributeOrNull(Attribute attribute)
{
if (attribute is EmailAddressAttribute)
{
return "email";
}
if (attribute is UrlAttribute)
{
return "url";
}
if (attribute is HiddenInputAttribute)
{
return "hidden";
}
if (attribute is PhoneAttribute)
{
return "tel";
}
if (attribute is DataTypeAttribute dataTypeAttribute)
{
switch (dataTypeAttribute.DataType)
{
case DataType.Password:
return "password";
case DataType.Date:
return "date";
case DataType.Time:
return "time";
case DataType.EmailAddress:
return "email";
case DataType.Url:
return "url";
case DataType.PhoneNumber:
return "tel";
case DataType.DateTime:
return "datetime-local";
}
}
return null;
}
private static string GetInputTypeFromTypeOrNull(Type type)
{
if (type == typeof(bool))
{
return "checkbox";
}
if (type == typeof(DateTime))
{
return "datetime-local";
}
if (type == typeof(int) ||
type == typeof(long) ||
type == typeof(byte) ||
type == typeof(sbyte) ||
type == typeof(short) ||
type == typeof(ushort) ||
type == typeof(uint) ||
type == typeof(long) ||
type == typeof(ulong))
{
return "number";
}
return null;
}
}
}

56
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<HttpResponseMessage> GetHttpResponseMessageWithRetryAsync<T>
(
string url,
CancellationToken? cancellationToken = null,
ILogger<T> logger = null,
IEnumerable<TimeSpan> 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));
}
}
}

39
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<AbpIoApiKeyService> _logger;
private DeveloperApiKeyResult _apiKeyResult = null;
public AbpIoApiKeyService(IJsonSerializer jsonSerializer, IRemoteServiceExceptionHandler remoteServiceExceptionHandler, ILogger<AbpIoApiKeyService> logger)
public AbpIoApiKeyService(
IJsonSerializer jsonSerializer,
ICancellationTokenProvider cancellationTokenProvider,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler,
ILogger<AbpIoApiKeyService> logger)
{
JsonSerializer = jsonSerializer;
RemoteServiceExceptionHandler = remoteServiceExceptionHandler;
_logger = logger;
CancellationTokenProvider = cancellationTokenProvider;
}
public async Task<DeveloperApiKeyResult> 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)
{

97
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<string> _proPackageList;
private DeveloperApiKeyResult _apiKeyResult;
public NuGetService(
IJsonSerializer jsonSerializer,
@ -45,20 +41,20 @@ namespace Volo.Abp.Cli.NuGet
public async Task<SemanticVersion> GetLatestVersionOrNullAsync(string packageId, bool includePreviews = false, bool includeNightly = false)
{
List<string> 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<string> GetNuGetUrlForCommercialPackage(string packageId)
{
var apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync();
return CliUrls.GetNuGetPackageInfoUrl(apiKeyResult.ApiKey, packageId);
}
if (_apiKeyResult == null)
{
_apiKeyResult = await _apiKeyService.GetApiKeyOrNullAsync();
}
private async Task<HttpResponseMessage> 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<List<string>> 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<List<string>>(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<List<string>>(await responseMessage.Content.ReadAsStringAsync());
Logger.LogError(exceptionMessage);
return null;
}
public class NuGetVersionResultDto

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/MyGetPackageListFinder.cs

@ -20,7 +20,7 @@ namespace Volo.Abp.Cli.ProjectModification
Logger = NullLogger<MyGetPackageListFinder>.Instance;
}
public async Task<MyGetApiResponse> GetPackages()
public async Task<MyGetApiResponse> GetPackagesAsync()
{
if (_response != null)
{

62
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<NpmPackagesUpdater> Logger { get; set; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
private readonly PackageJsonFileFinder _packageJsonFileFinder;
private readonly NpmGlobalPackagesChecker _npmGlobalPackagesChecker;
private readonly MyGetPackageListFinder _myGetPackageListFinder;
private readonly Dictionary<string, string> _fileVersionStorage = new Dictionary<string, string>();
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<NpmPackagesUpdater>.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<bool> UpdatePackagesInFile(string file, bool includePreviews = false, bool switchToStable = false)
protected virtual async Task<bool> 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<bool> TryUpdatePackage(string file, JProperty package,
bool includePreviews = false, bool switchToStable = false)
protected virtual async Task<bool> 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<string> GetLatestVersion(JProperty package, string currentVersion,
bool includePreviews = false, bool switchToStable = false)
protected virtual async Task<string> 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;

27
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<string> 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<string> GetLatestVersionFromMyGet(string packageId)
{
var myGetPack = await _myGetPackageListFinder.GetPackagesAsync();
return myGetPack.Packages.FirstOrDefault(p => p.Id == packageId)?.Versions.LastOrDefault();
}
}
}

2
framework/src/Volo.Abp.Core/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs

@ -1,6 +1,6 @@
namespace Microsoft.Extensions.Localization
{
public static class AbpStringLocalizerFactoryExtensions
public static class AbpCoreStringLocalizerFactoryExtensions
{
public static IStringLocalizer Create<TResource>(this IStringLocalizerFactory localizerFactory)
{

18
framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs

@ -35,6 +35,24 @@ namespace Volo.Abp.Localization
});
}
public static bool IsValidCultureCode(string cultureCode)
{
if (cultureCode.IsNullOrWhiteSpace())
{
return false;
}
try
{
CultureInfo.GetCultureInfo(cultureCode);
return true;
}
catch (CultureNotFoundException)
{
return false;
}
}
public static string GetBaseCultureName(string cultureName)
{
return cultureName.Contains("-")

133
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
namespace Volo.Abp.Reflection
{
@ -131,5 +132,137 @@ namespace Volo.Abp.Reflection
type == typeof(TimeSpan) ||
type == typeof(Guid);
}
public static T GetDefaultValue<T>()
{
return default;
}
public static object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
public static string GetFullNameHandlingNullableAndGenerics([NotNull] Type type)
{
Check.NotNull(type, nameof(type));
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return type.GenericTypeArguments[0].FullName + "?";
}
if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetFullNameHandlingNullableAndGenerics).JoinAsString(",")}>";
}
return type.FullName;
}
public static string GetSimplifiedName([NotNull] Type type)
{
Check.NotNull(type, nameof(type));
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return GetSimplifiedName(type.GenericTypeArguments[0]) + "?";
}
if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetSimplifiedName).JoinAsString(",")}>";
}
if (type == typeof(string))
{
return "string";
}
else if (type == typeof(int))
{
return "number";
}
else if (type == typeof(long))
{
return "number";
}
else if (type == typeof(bool))
{
return "boolean";
}
else if (type == typeof(char))
{
return "string";
}
else if (type == typeof(double))
{
return "number";
}
else if (type == typeof(float))
{
return "number";
}
else if (type == typeof(decimal))
{
return "number";
}
else if (type == typeof(DateTime))
{
return "string";
}
else if (type == typeof(DateTimeOffset))
{
return "string";
}
else if (type == typeof(TimeSpan))
{
return "string";
}
else if (type == typeof(Guid))
{
return "string";
}
else if (type == typeof(byte))
{
return "number";
}
else if (type == typeof(sbyte))
{
return "number";
}
else if (type == typeof(short))
{
return "number";
}
else if (type == typeof(ushort))
{
return "number";
}
else if (type == typeof(uint))
{
return "number";
}
else if (type == typeof(ulong))
{
return "number";
}
else if (type == typeof(IntPtr))
{
return "number";
}
else if (type == typeof(UIntPtr))
{
return "number";
}
return type.FullName;
}
}
}

9
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs

@ -25,8 +25,9 @@ namespace Volo.Abp.Domain.Entities
protected AggregateRoot()
{
ExtraProperties = new Dictionary<string, object>();
ConcurrencyStamp = Guid.NewGuid().ToString("N");
ExtraProperties = new Dictionary<string, object>();
this.SetDefaultsForExtraProperties();
}
protected virtual void AddLocalEvent(object eventData)
@ -85,15 +86,17 @@ namespace Volo.Abp.Domain.Entities
protected AggregateRoot()
{
ExtraProperties = new Dictionary<string, object>();
ConcurrencyStamp = Guid.NewGuid().ToString("N");
ExtraProperties = new Dictionary<string, object>();
this.SetDefaultsForExtraProperties();
}
protected AggregateRoot(TKey id)
: base(id)
{
ExtraProperties = new Dictionary<string, object>();
ConcurrencyStamp = Guid.NewGuid().ToString("N");
ExtraProperties = new Dictionary<string, object>();
this.SetDefaultsForExtraProperties();
}
protected virtual void AddLocalEvent(object eventData)

4
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/en.json

@ -18,6 +18,8 @@
"Description:Abp.Mailing.Smtp.Password": "The password for the user name associated with the credentials.",
"Description:Abp.Mailing.Smtp.Domain": "The domain or computer name that verifies the credentials.",
"Description:Abp.Mailing.Smtp.EnableSsl": "Whether the SmtpClient uses Secure Sockets Layer (SSL) to encrypt the connection.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Whether the DefaultCredentials are sent with requests."
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Whether the DefaultCredentials are sent with requests.",
"TextTemplate:StandardEmailTemplates.Layout": "Default email layout template",
"TextTemplate:StandardEmailTemplates.Message": "Simple message template for emails"
}
}

25
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/tr.json

@ -0,0 +1,25 @@
{
"culture": "tr",
"texts": {
"DisplayName:Abp.Mailing.DefaultFromAddress": "Varsayılan gönderici adresi",
"DisplayName:Abp.Mailing.DefaultFromDisplayName": "Varsayılan gönderici adı",
"DisplayName:Abp.Mailing.Smtp.Host": "Sunucu",
"DisplayName:Abp.Mailing.Smtp.Port": "Port",
"DisplayName:Abp.Mailing.Smtp.UserName": "Kullanıcı adı",
"DisplayName:Abp.Mailing.Smtp.Password": "Şifre",
"DisplayName:Abp.Mailing.Smtp.Domain": "Domain",
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "SSL aktif",
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "Varsayılan kimlik kullan",
"Description:Abp.Mailing.DefaultFromAddress": "Varsayılan gönderici adresi",
"Description:Abp.Mailing.DefaultFromDisplayName": "Varsayılan gönderici adı",
"Description:Abp.Mailing.Smtp.Host": "SMTP üzerinden e-posta göndermek için kullanılacak sunucunun IP adresi ya da adı.",
"Description:Abp.Mailing.Smtp.Port": "Sunucunun SMTP portu.",
"Description:Abp.Mailing.Smtp.UserName": "Varsayılan kimlik kullanılmaması durumunda kullanılacak kullanıcı adı.",
"Description:Abp.Mailing.Smtp.Password": "Varsayılan kimlik kullanılmaması durumunda kullanılacak şifre.",
"Description:Abp.Mailing.Smtp.Domain": "Kimlik bilgilerinin doğrulanacağı sunucu/domain.",
"Description:Abp.Mailing.Smtp.EnableSsl": "Email gönderiminde SSL kullanılıp kullanılmayacağı.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Varsayılan kimlik bilgilerinin kullanılıp kullanılmayacağı.",
"TextTemplate:StandardEmailTemplates.Layout": "Varsayılan e-posta layout şablonu",
"TextTemplate:StandardEmailTemplates.Message": "Basit bir mesaj göndermek için e-posta şablonu"
}
}

0
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl → framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout.tpl

0
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl → framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message.tpl

14
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs → framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplateDefinitionProvider.cs

@ -1,25 +1,27 @@
using Volo.Abp.TextTemplating;
using Volo.Abp.Emailing.Localization;
using Volo.Abp.Localization;
using Volo.Abp.TextTemplating;
namespace Volo.Abp.Emailing.Templates
{
public class DefaultEmailTemplateProvider : TemplateDefinitionProvider
public class StandardEmailTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition(
StandardEmailTemplates.Layout,
defaultCultureName: "en",
displayName: LocalizableString.Create<EmailingResource>("TextTemplate:StandardEmailTemplates.Layout"),
isLayout: true
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Layout")
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Layout.tpl", true)
);
context.Add(
new TemplateDefinition(
StandardEmailTemplates.Message,
defaultCultureName: "en",
displayName: LocalizableString.Create<EmailingResource>("TextTemplate:StandardEmailTemplates.Message"),
layout: StandardEmailTemplates.Layout
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Message")
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Message.tpl", true)
);
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -204,7 +204,7 @@ namespace Volo.Abp.EntityFrameworkCore
var currentValue = e.Entry.CurrentValues[property.Name];
if (currentValue != null)
{
entity.SetProperty(property.Name, currentValue);
entity.ExtraProperties[property.Name] = currentValue;
}
}
}
@ -265,7 +265,11 @@ namespace Volo.Abp.EntityFrameworkCore
return;
}
foreach (var property in objectExtension.GetProperties())
var efMappedProperties = ObjectExtensionManager.Instance
.GetProperties(entityType)
.Where(p => p.IsMappedToFieldForEfCore());
foreach (var property in efMappedProperties)
{
if (!entity.HasProperty(property.Name))
{

5
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs

@ -1,5 +1,6 @@
using System;
using System.Reflection;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling
{
@ -29,8 +30,8 @@ namespace Volo.Abp.Http.Modeling
{
Name = parameterInfo.Name,
TypeAsString = parameterInfo.ParameterType.GetFullNameWithAssemblyName(),
Type = parameterInfo.ParameterType != null ? ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(parameterInfo.ParameterType) : null,
TypeSimple = parameterInfo.ParameterType != null ? ModelingTypeHelper.GetSimplifiedName(parameterInfo.ParameterType) : null,
Type = parameterInfo.ParameterType != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(parameterInfo.ParameterType) : null,
TypeSimple = parameterInfo.ParameterType != null ? TypeHelper.GetSimplifiedName(parameterInfo.ParameterType) : null,
IsOptional = parameterInfo.IsOptional,
DefaultValue = parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : null
};

127
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModelingTypeHelper.cs

@ -1,127 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.Abp.Http.Modeling
{
public static class ModelingTypeHelper
{
public static string GetFullNameHandlingNullableAndGenerics([NotNull] Type type)
{
Check.NotNull(type, nameof(type));
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return type.GenericTypeArguments[0].FullName + "?";
}
if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetFullNameHandlingNullableAndGenerics).JoinAsString(",")}>";
}
return type.FullName;
}
public static string GetSimplifiedName([NotNull] Type type)
{
Check.NotNull(type, nameof(type));
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return GetSimplifiedName(type.GenericTypeArguments[0]) + "?";
}
if (type.IsGenericType)
{
var genericType = type.GetGenericTypeDefinition();
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetSimplifiedName).JoinAsString(",")}>";
}
if (type == typeof(string))
{
return "string";
}
else if (type == typeof(int))
{
return "number";
}
else if (type == typeof(long))
{
return "number";
}
else if (type == typeof(bool))
{
return "boolean";
}
else if (type == typeof(char))
{
return "string";
}
else if (type == typeof(double))
{
return "number";
}
else if (type == typeof(float))
{
return "number";
}
else if (type == typeof(decimal))
{
return "number";
}
else if (type == typeof(DateTime))
{
return "string";
}
else if (type == typeof(DateTimeOffset))
{
return "string";
}
else if (type == typeof(TimeSpan))
{
return "string";
}
else if (type == typeof(Guid))
{
return "string";
}
else if (type == typeof(byte))
{
return "number";
}
else if (type == typeof(sbyte))
{
return "number";
}
else if (type == typeof(short))
{
return "number";
}
else if (type == typeof(ushort))
{
return "number";
}
else if (type == typeof(uint))
{
return "number";
}
else if (type == typeof(ulong))
{
return "number";
}
else if (type == typeof(IntPtr))
{
return "number";
}
else if (type == typeof(UIntPtr))
{
return "number";
}
return type.FullName;
}
}
}

5
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs

@ -1,4 +1,5 @@
using System;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling
{
@ -34,8 +35,8 @@ namespace Volo.Abp.Http.Modeling
{
Name = name,
NameOnMethod = nameOnMethod,
Type = type != null ? ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(type) : null,
TypeSimple = type != null ? ModelingTypeHelper.GetSimplifiedName(type) : null,
Type = type != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(type) : null,
TypeSimple = type != null ? TypeHelper.GetSimplifiedName(type) : null,
IsOptional = isOptional,
DefaultValue = defaultValue,
ConstraintTypes = constraintTypes,

12
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs

@ -21,18 +21,18 @@ namespace Volo.Abp.Http.Modeling
if (TypeHelper.IsEnumerable(propertyInfo.PropertyType, out var itemType, includePrimitives: false))
{
typeName = $"[{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(itemType)}]";
simpleTypeName = $"[{ModelingTypeHelper.GetSimplifiedName(itemType)}]";
typeName = $"[{TypeHelper.GetFullNameHandlingNullableAndGenerics(itemType)}]";
simpleTypeName = $"[{TypeHelper.GetSimplifiedName(itemType)}]";
}
else if (TypeHelper.IsDictionary(propertyInfo.PropertyType, out var keyType, out var valueType))
{
typeName = $"{{{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(keyType)}:{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(valueType)}}}";
simpleTypeName = $"{{{ModelingTypeHelper.GetSimplifiedName(keyType)}:{ModelingTypeHelper.GetSimplifiedName(valueType)}}}";
typeName = $"{{{TypeHelper.GetFullNameHandlingNullableAndGenerics(keyType)}:{TypeHelper.GetFullNameHandlingNullableAndGenerics(valueType)}}}";
simpleTypeName = $"{{{TypeHelper.GetSimplifiedName(keyType)}:{TypeHelper.GetSimplifiedName(valueType)}}}";
}
else
{
typeName = ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(propertyInfo.PropertyType);
simpleTypeName = ModelingTypeHelper.GetSimplifiedName(propertyInfo.PropertyType);
typeName = TypeHelper.GetFullNameHandlingNullableAndGenerics(propertyInfo.PropertyType);
simpleTypeName = TypeHelper.GetSimplifiedName(propertyInfo.PropertyType);
}
return new PropertyApiDescriptionModel

5
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs

@ -1,4 +1,5 @@
using System;
using Volo.Abp.Reflection;
using Volo.Abp.Threading;
namespace Volo.Abp.Http.Modeling
@ -21,8 +22,8 @@ namespace Volo.Abp.Http.Modeling
return new ReturnValueApiDescriptionModel
{
Type = ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(unwrappedType),
TypeSimple = ModelingTypeHelper.GetSimplifiedName(unwrappedType)
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(unwrappedType),
TypeSimple = TypeHelper.GetSimplifiedName(unwrappedType)
};
}
}

3
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling
{
@ -32,7 +33,7 @@ namespace Volo.Abp.Http.Modeling
var typeModel = new TypeApiDescriptionModel
{
IsEnum = type.IsEnum,
BaseType = baseType != null ? ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(baseType) : null
BaseType = baseType != null ? TypeHelper.GetFullNameHandlingNullableAndGenerics(baseType) : null
};
if (typeModel.IsEnum)

11
framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs

@ -0,0 +1,11 @@
namespace Microsoft.Extensions.Localization
{
public static class AbpStringLocalizerFactoryExtensions
{
public static IStringLocalizer CreateDefaultOrNull(this IStringLocalizerFactory localizerFactory)
{
return (localizerFactory as IAbpStringLocalizerFactoryWithDefaultResourceSupport)
?.CreateDefaultOrNull();
}
}
}

10
framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactoryWithDefaultResourceSupport.cs

@ -0,0 +1,10 @@
using JetBrains.Annotations;
namespace Microsoft.Extensions.Localization
{
public interface IAbpStringLocalizerFactoryWithDefaultResourceSupport
{
[CanBeNull]
IStringLocalizer CreateDefaultOrNull();
}
}

36
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/HasNameWithLocalizableDisplayNameExtensions.cs

@ -0,0 +1,36 @@
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization
{
public static class HasNameWithLocalizableDisplayNameExtensions
{
[NotNull]
public static string GetLocalizedDisplayName(
[NotNull] this IHasNameWithLocalizableDisplayName source,
[NotNull] IStringLocalizerFactory stringLocalizerFactory,
[CanBeNull] string localizationNamePrefix = "DisplayName:")
{
if (source.DisplayName != null)
{
return source.DisplayName.Localize(stringLocalizerFactory);
}
var defaultStringLocalizer = stringLocalizerFactory.CreateDefaultOrNull();
if (defaultStringLocalizer == null)
{
return source.Name;
}
var localizedString = defaultStringLocalizer[$"{localizationNamePrefix}{source.Name}"];
if (!localizedString.ResourceNotFound ||
localizationNamePrefix.IsNullOrEmpty())
{
return localizedString;
}
return defaultStringLocalizer[source.Name];
}
}
}

13
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IHasNameWithLocalizableDisplayName.cs

@ -0,0 +1,13 @@
using JetBrains.Annotations;
namespace Volo.Abp.Localization
{
public interface IHasNameWithLocalizableDisplayName
{
[NotNull]
public string Name { get; }
[CanBeNull]
public ILocalizableString DisplayName { get; }
}
}

8
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationOptions.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Volo.Abp.Collections;
namespace Volo.Abp.Localization
@ -7,6 +8,11 @@ namespace Volo.Abp.Localization
{
public LocalizationResourceDictionary Resources { get; }
/// <summary>
/// Used as the default resource when resource was not specified on a localization operation.
/// </summary>
public Type DefaultResourceType { get; set; }
public ITypeList<ILocalizationResourceContributor> GlobalContributors { get; }
public List<LanguageInfo> Languages { get; }

47
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs

@ -9,13 +9,12 @@ using Microsoft.Extensions.Options;
namespace Volo.Abp.Localization
{
public class AbpStringLocalizerFactory : IStringLocalizerFactory
public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLocalizerFactoryWithDefaultResourceSupport
{
private readonly ResourceManagerStringLocalizerFactory _innerFactory;
private readonly AbpLocalizationOptions _abpLocalizationOptions;
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<Type, StringLocalizerCacheItem> _localizerCache;
protected internal AbpLocalizationOptions AbpLocalizationOptions { get; }
protected ResourceManagerStringLocalizerFactory InnerFactory { get; }
protected IServiceProvider ServiceProvider { get; }
protected ConcurrentDictionary<Type, StringLocalizerCacheItem> LocalizerCache { get; }
//TODO: It's better to use decorator pattern for IStringLocalizerFactory instead of getting ResourceManagerStringLocalizerFactory as a dependency.
public AbpStringLocalizerFactory(
@ -23,29 +22,29 @@ namespace Volo.Abp.Localization
IOptions<AbpLocalizationOptions> abpLocalizationOptions,
IServiceProvider serviceProvider)
{
_innerFactory = innerFactory;
_serviceProvider = serviceProvider;
_abpLocalizationOptions = abpLocalizationOptions.Value;
InnerFactory = innerFactory;
ServiceProvider = serviceProvider;
AbpLocalizationOptions = abpLocalizationOptions.Value;
_localizerCache = new ConcurrentDictionary<Type, StringLocalizerCacheItem>();
LocalizerCache = new ConcurrentDictionary<Type, StringLocalizerCacheItem>();
}
public virtual IStringLocalizer Create(Type resourceType)
{
var resource = _abpLocalizationOptions.Resources.GetOrDefault(resourceType);
var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceType);
if (resource == null)
{
return _innerFactory.Create(resourceType);
return InnerFactory.Create(resourceType);
}
if (_localizerCache.TryGetValue(resourceType, out var cacheItem))
if (LocalizerCache.TryGetValue(resourceType, out var cacheItem))
{
return cacheItem.Localizer;
}
lock (_localizerCache)
lock (LocalizerCache)
{
return _localizerCache.GetOrAdd(
return LocalizerCache.GetOrAdd(
resourceType,
_ => CreateStringLocalizerCacheItem(resource)
).Localizer;
@ -54,12 +53,12 @@ namespace Volo.Abp.Localization
private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResource resource)
{
foreach (var globalContributor in _abpLocalizationOptions.GlobalContributors)
foreach (var globalContributor in AbpLocalizationOptions.GlobalContributors)
{
resource.Contributors.Add((ILocalizationResourceContributor) Activator.CreateInstance(globalContributor));
}
using (var scope = _serviceProvider.CreateScope())
using (var scope = ServiceProvider.CreateScope())
{
var context = new LocalizationResourceInitializationContext(resource, scope.ServiceProvider);
@ -81,7 +80,7 @@ namespace Volo.Abp.Localization
{
//TODO: Investigate when this is called?
return _innerFactory.Create(baseName, location);
return InnerFactory.Create(baseName, location);
}
internal static void Replace(IServiceCollection services)
@ -90,7 +89,7 @@ namespace Volo.Abp.Localization
services.AddSingleton<ResourceManagerStringLocalizerFactory>();
}
private class StringLocalizerCacheItem
protected class StringLocalizerCacheItem
{
public AbpDictionaryBasedStringLocalizer Localizer { get; }
@ -99,5 +98,15 @@ namespace Volo.Abp.Localization
Localizer = localizer;
}
}
public IStringLocalizer CreateDefaultOrNull()
{
if (AbpLocalizationOptions.DefaultResourceType == null)
{
return null;
}
return Create(AbpLocalizationOptions.DefaultResourceType);
}
}
}

9
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableString.cs

@ -1,18 +1,21 @@
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization
{
public class LocalizableString : ILocalizableString
{
[CanBeNull]
public Type ResourceType { get; }
[NotNull]
public string Name { get; }
public LocalizableString(Type resourceType, string name)
public LocalizableString(Type resourceType, [NotNull] string name)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
ResourceType = resourceType;
Name = name;
}
public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory)
@ -20,7 +23,7 @@ namespace Volo.Abp.Localization
return stringLocalizerFactory.Create(ResourceType)[Name];
}
public static LocalizableString Create<TResource>(string name)
public static LocalizableString Create<TResource>([NotNull] string name)
{
return new LocalizableString(typeof(TResource), name);
}

5
framework/src/Volo.Abp.ObjectExtending/Volo.Abp.ObjectExtending.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
@ -15,7 +15,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Validation.Abstractions\Volo.Abp.Validation.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" />
</ItemGroup>
</Project>

43
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Volo.Abp.DynamicProxy;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Reflection;
namespace Volo.Abp.Data
@ -36,10 +38,20 @@ namespace Volo.Abp.Data
throw new AbpException("GetProperty<TProperty> does not support non-primitive types. Use non-generic GetProperty method and handle type casting manually.");
}
public static TSource SetProperty<TSource>(this TSource source, string name, object value)
public static TSource SetProperty<TSource>(
this TSource source,
string name,
object value,
bool validate = true)
where TSource : IHasExtraProperties
{
if (validate)
{
ExtensibleObjectValidator.CheckValue(source, name, value);
}
source.ExtraProperties[name] = value;
return source;
}
@ -49,5 +61,34 @@ namespace Volo.Abp.Data
source.ExtraProperties.Remove(name);
return source;
}
public static TSource SetDefaultsForExtraProperties<TSource>(this TSource source, Type objectType = null)
where TSource : IHasExtraProperties
{
var properties = ObjectExtensionManager.Instance
.GetProperties(objectType ?? typeof(TSource));
foreach (var property in properties)
{
if (source.HasProperty(property.Name))
{
continue;
}
source.ExtraProperties[property.Name] = TypeHelper.GetDefaultValue(property.Type);
}
return source;
}
public static void SetDefaultsForExtraProperties(object source, Type objectType)
{
if (!(source is IHasExtraProperties))
{
throw new ArgumentException($"Given {nameof(source)} object does not implement the {nameof(IHasExtraProperties)} interface!", nameof(source));
}
((IHasExtraProperties) source).SetDefaultsForExtraProperties(objectType);
}
}
}

6
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/AbpObjectExtendingModule.cs

@ -1,7 +1,13 @@
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Volo.Abp.Localization;
namespace Volo.Abp.ObjectExtending
{
[DependsOn(
typeof(AbpLocalizationAbstractionsModule),
typeof(AbpValidationAbstractionsModule)
)]
public class AbpObjectExtendingModule : AbpModule
{

12
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Data;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.ObjectExtending
{
@ -11,8 +12,19 @@ namespace Volo.Abp.ObjectExtending
public Dictionary<string, object> ExtraProperties { get; protected set; }
public ExtensibleObject()
: this(true)
{
}
public ExtensibleObject(bool setDefaultsForExtraProperties)
{
ExtraProperties = new Dictionary<string, object>();
if (setDefaultsForExtraProperties)
{
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

149
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs

@ -4,12 +4,29 @@ using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Data;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Validation;
namespace Volo.Abp.ObjectExtending
{
public static class ExtensibleObjectValidator
{
[NotNull]
public static void CheckValue(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
[CanBeNull] object value)
{
var validationErrors = GetValidationErrors(
extensibleObject,
propertyName,
value
);
if (validationErrors.Any())
{
throw new AbpValidationException(validationErrors);
}
}
public static bool IsValid(
[NotNull] IHasExtraProperties extensibleObject,
[CanBeNull] ValidationContext objectValidationContext = null)
@ -20,13 +37,27 @@ namespace Volo.Abp.ObjectExtending
).Any();
}
public static bool IsValid(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
[CanBeNull] object value,
[CanBeNull] ValidationContext objectValidationContext = null)
{
return GetValidationErrors(
extensibleObject,
propertyName,
value,
objectValidationContext
).Any();
}
[NotNull]
public static List<ValidationResult> GetValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[CanBeNull] ValidationContext objectValidationContext = null)
{
var validationErrors = new List<ValidationResult>();
AddValidationErrors(
extensibleObject,
validationErrors,
@ -36,6 +67,26 @@ namespace Volo.Abp.ObjectExtending
return validationErrors;
}
[NotNull]
public static List<ValidationResult> GetValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
[CanBeNull] object value,
[CanBeNull] ValidationContext objectValidationContext = null)
{
var validationErrors = new List<ValidationResult>();
AddValidationErrors(
extensibleObject,
validationErrors,
propertyName,
value,
objectValidationContext
);
return validationErrors;
}
public static void AddValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] List<ValidationResult> validationErrors,
@ -78,10 +129,55 @@ namespace Volo.Abp.ObjectExtending
);
}
public static void AddValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] List<ValidationResult> validationErrors,
[NotNull] string propertyName,
[CanBeNull] object value,
[CanBeNull] ValidationContext objectValidationContext = null)
{
Check.NotNull(extensibleObject, nameof(extensibleObject));
Check.NotNull(validationErrors, nameof(validationErrors));
Check.NotNullOrWhiteSpace(propertyName, nameof(propertyName));
if (objectValidationContext == null)
{
objectValidationContext = new ValidationContext(
extensibleObject,
null,
new Dictionary<object, object>()
);
}
var objectType = ProxyHelper.UnProxy(extensibleObject).GetType();
var objectExtensionInfo = ObjectExtensionManager.Instance
.GetOrNull(objectType);
if (objectExtensionInfo == null)
{
return;
}
var property = objectExtensionInfo.GetPropertyOrNull(propertyName);
if (property == null)
{
return;
}
AddPropertyValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property,
value
);
}
private static void AddPropertyValidationErrors(
IHasExtraProperties extensibleObject,
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ValidationContext objectValidationContext,
ObjectExtensionInfo objectExtensionInfo)
{
var properties = objectExtensionInfo.GetProperties();
@ -92,38 +188,50 @@ namespace Volo.Abp.ObjectExtending
foreach (var property in properties)
{
AddPropertyValidationErrors(extensibleObject, validationErrors, objectValidationContext, property);
AddPropertyValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property,
extensibleObject.GetProperty(property.Name)
);
}
}
private static void AddPropertyValidationErrors(
IHasExtraProperties extensibleObject,
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property)
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property,
object value)
{
AddPropertyValidationAttributeErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property
property,
value
);
ExecuteCustomPropertyValidationActions(
extensibleObject,
validationErrors,
objectValidationContext,
property
property,
value
);
}
private static void AddPropertyValidationAttributeErrors(
IHasExtraProperties extensibleObject,
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property)
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property,
object value)
{
if (!property.ValidationAttributes.Any())
var validationAttributes = property.GetValidationAttributes();
if (!validationAttributes.Any())
{
return;
}
@ -134,10 +242,10 @@ namespace Volo.Abp.ObjectExtending
MemberName = property.Name
};
foreach (var attribute in property.ValidationAttributes)
foreach (var attribute in validationAttributes)
{
var result = attribute.GetValidationResult(
extensibleObject.GetProperty(property.Name),
value,
propertyValidationContext
);
@ -152,7 +260,8 @@ namespace Volo.Abp.ObjectExtending
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property)
ObjectExtensionPropertyInfo property,
object value)
{
if (!property.Validators.Any())
{
@ -164,7 +273,7 @@ namespace Volo.Abp.ObjectExtending
extensibleObject,
validationErrors,
objectValidationContext,
extensibleObject.GetProperty(property.Name)
value
);
foreach (var validator in property.Validators)
@ -174,9 +283,9 @@ namespace Volo.Abp.ObjectExtending
}
private static void ExecuteCustomObjectValidationActions(
IHasExtraProperties extensibleObject,
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ValidationContext objectValidationContext,
ObjectExtensionInfo objectExtensionInfo)
{
if (!objectExtensionInfo.Validators.Any())

62
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfiguration.cs

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class EntityExtensionConfiguration
{
[NotNull]
protected ExtensionPropertyConfigurationDictionary Properties { get; }
[NotNull]
public List<Action<ObjectExtensionValidationContext>> Validators { get; }
public Dictionary<string, object> Configuration { get; }
public EntityExtensionConfiguration()
{
Properties = new ExtensionPropertyConfigurationDictionary();
Validators = new List<Action<ObjectExtensionValidationContext>>();
Configuration = new Dictionary<string, object>();
}
[NotNull]
public virtual EntityExtensionConfiguration AddOrUpdateProperty<TProperty>(
[NotNull] string propertyName,
[CanBeNull] Action<ExtensionPropertyConfiguration> configureAction = null)
{
return AddOrUpdateProperty(
typeof(TProperty),
propertyName,
configureAction
);
}
[NotNull]
public virtual EntityExtensionConfiguration AddOrUpdateProperty(
[NotNull] Type propertyType,
[NotNull] string propertyName,
[CanBeNull] Action<ExtensionPropertyConfiguration> configureAction = null)
{
Check.NotNull(propertyType, nameof(propertyType));
Check.NotNull(propertyName, nameof(propertyName));
var propertyInfo = Properties.GetOrAdd(
propertyName,
() => new ExtensionPropertyConfiguration(this, propertyType, propertyName)
);
configureAction?.Invoke(propertyInfo);
return this;
}
[NotNull]
public virtual ImmutableList<ExtensionPropertyConfiguration> GetProperties()
{
return Properties.Values.ToImmutableList();
}
}
}

9
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfigurationDictionary.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class EntityExtensionConfigurationDictionary : Dictionary<string, EntityExtensionConfiguration>
{
}
}

23
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiConfiguration.cs

@ -0,0 +1,23 @@
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyApiConfiguration
{
[NotNull]
public ExtensionPropertyApiGetConfiguration OnGet { get; }
[NotNull]
public ExtensionPropertyApiCreateConfiguration OnCreate { get; }
[NotNull]
public ExtensionPropertyApiUpdateConfiguration OnUpdate { get; }
public ExtensionPropertyApiConfiguration()
{
OnGet = new ExtensionPropertyApiGetConfiguration();
OnCreate = new ExtensionPropertyApiCreateConfiguration();
OnUpdate = new ExtensionPropertyApiUpdateConfiguration();
}
}
}

10
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiCreateConfiguration.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyApiCreateConfiguration
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

10
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiGetConfiguration.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyApiGetConfiguration
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

10
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyApiUpdateConfiguration.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyApiUpdateConfiguration
{
/// <summary>
/// Default: true.
/// </summary>
public bool IsAvailable { get; set; } = true;
}
}

66
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyConfiguration : IHasNameWithLocalizableDisplayName
{
[NotNull]
public EntityExtensionConfiguration EntityExtensionConfiguration { get; }
[NotNull]
public string Name { get; }
[NotNull]
public Type Type { get; }
[NotNull]
public List<Attribute> Attributes { get; }
[NotNull]
public List<Action<ObjectExtensionPropertyValidationContext>> Validators { get; }
[CanBeNull]
public ILocalizableString DisplayName { get; set; }
[NotNull]
public Dictionary<string, object> Configuration { get; }
/// <summary>
/// Single point to enable/disable this property for the clients (UI and API).
/// If this is false, the configuration made in the <see cref="UI"/> and the <see cref="Api"/>
/// properties are not used.
/// Default: true.
/// </summary>
public bool IsAvailableToClients { get; set; } = true;
[NotNull]
public ExtensionPropertyEntityConfiguration Entity { get; }
[NotNull]
public ExtensionPropertyUiConfiguration UI { get; }
[NotNull]
public ExtensionPropertyApiConfiguration Api { get; }
public ExtensionPropertyConfiguration(
[NotNull] EntityExtensionConfiguration entityExtensionConfiguration,
[NotNull] Type type,
[NotNull] string name)
{
EntityExtensionConfiguration = Check.NotNull(entityExtensionConfiguration, nameof(entityExtensionConfiguration));
Type = Check.NotNull(type, nameof(type));
Name = Check.NotNull(name, nameof(name));
Configuration = new Dictionary<string, object>();
Attributes = new List<Attribute>();
Validators = new List<Action<ObjectExtensionPropertyValidationContext>>();
Entity = new ExtensionPropertyEntityConfiguration();
UI = new ExtensionPropertyUiConfiguration();
Api = new ExtensionPropertyApiConfiguration();
}
}
}

9
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationDictionary.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyConfigurationDictionary : Dictionary<string, ExtensionPropertyConfiguration>
{
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save