Browse Source

Merge branch 'dev' into berkan-engin/community

pull/4957/head
berkansasmaz 6 years ago
parent
commit
feeb359f40
  1. 38
      .github/lock.yml
  2. 15
      .github/stale.yml
  3. 11
      .github/workflows/angular.yml
  4. 4
      common.props
  5. 12
      docs/en/API/Application-Configuration.md
  6. 30
      docs/en/Authentication/Social-External-Logins.md
  7. 2
      docs/en/Authorization.md
  8. 5
      docs/en/Background-Workers-Quartz.md
  9. 2
      docs/en/Best-Practices/Application-Services.md
  10. 2
      docs/en/Blob-Storing-Database.md
  11. 2
      docs/en/Entity-Framework-Core-Migrations.md
  12. 2
      docs/en/Modules/Docs.md
  13. 8
      docs/en/Startup-Templates/Application.md
  14. 2
      docs/en/Startup-Templates/Module.md
  15. 46
      docs/en/Tutorials/Part-1.md
  16. 2
      docs/en/Tutorials/Part-10.md
  17. 87
      docs/en/Tutorials/Part-8.md
  18. 9
      docs/en/docs-nav.json
  19. 12
      docs/zh-Hans/API/Application-Configuration.md
  20. 30
      docs/zh-Hans/Authentication/Social-External-Logins.md
  21. 2
      docs/zh-Hans/Authorization.md
  22. 3
      docs/zh-Hans/Background-Workers-Quartz.md
  23. 2
      docs/zh-Hans/Best-Practices/Application-Services.md
  24. 2
      docs/zh-Hans/Blob-Storing-Database.md
  25. 2
      docs/zh-Hans/Entity-Framework-Core-Migrations.md
  26. 2
      docs/zh-Hans/Modules/Docs.md
  27. 8
      docs/zh-Hans/Startup-Templates/Application.md
  28. 2
      docs/zh-Hans/Startup-Templates/Module.md
  29. 923
      docs/zh-Hans/Tutorials/Part-1.md
  30. 1
      docs/zh-Hans/Tutorials/Part-10.md
  31. 1302
      docs/zh-Hans/Tutorials/Part-2.md
  32. 1233
      docs/zh-Hans/Tutorials/Part-3.md
  33. 255
      docs/zh-Hans/Tutorials/Part-4.md
  34. 1
      docs/zh-Hans/Tutorials/Part-5.md
  35. 1
      docs/zh-Hans/Tutorials/Part-6.md
  36. 1
      docs/zh-Hans/Tutorials/Part-7.md
  37. 1
      docs/zh-Hans/Tutorials/Part-8.md
  38. 1
      docs/zh-Hans/Tutorials/Part-9.md
  39. BIN
      docs/zh-Hans/Tutorials/images/bookstore-add-migration-authors.png
  40. BIN
      docs/zh-Hans/Tutorials/images/bookstore-added-author-to-book-list-angular.png
  41. BIN
      docs/zh-Hans/Tutorials/images/bookstore-added-author-to-book-list.png
  42. BIN
      docs/zh-Hans/Tutorials/images/bookstore-added-authors-to-modals.png
  43. BIN
      docs/zh-Hans/Tutorials/images/bookstore-angular-authors-page.png
  44. BIN
      docs/zh-Hans/Tutorials/images/bookstore-angular-service-proxy-author.png
  45. BIN
      docs/zh-Hans/Tutorials/images/bookstore-author-domain-layer.png
  46. BIN
      docs/zh-Hans/Tutorials/images/bookstore-author-permissions.png
  47. BIN
      docs/zh-Hans/Tutorials/images/bookstore-authors-page.png
  48. BIN
      docs/zh-Hans/Tutorials/images/bookstore-book-and-booktype.png
  49. BIN
      docs/zh-Hans/Tutorials/images/bookstore-book-list-3.png
  50. BIN
      docs/zh-Hans/Tutorials/images/bookstore-book-list.png
  51. BIN
      docs/zh-Hans/Tutorials/images/bookstore-dbmigrator-on-solution.png
  52. BIN
      docs/zh-Hans/Tutorials/images/bookstore-edit-button-2.png
  53. BIN
      docs/zh-Hans/Tutorials/images/bookstore-edit-delete-actions.png
  54. BIN
      docs/zh-Hans/Tutorials/images/bookstore-getlist-result-network.png
  55. BIN
      docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v3.png
  56. BIN
      docs/zh-Hans/Tutorials/images/bookstore-javascript-proxy-console.png
  57. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-author-modal.png
  58. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-book-button-2.png
  59. BIN
      docs/zh-Hans/Tutorials/images/bookstore-new-book-button-small.png
  60. BIN
      docs/zh-Hans/Tutorials/images/bookstore-permissions-ui.png
  61. BIN
      docs/zh-Hans/Tutorials/images/generated-proxies-2.png
  62. 49
      docs/zh-Hans/UI/Angular/Custom-Setting-Page.md
  63. 47
      docs/zh-Hans/UI/Angular/Localization.md
  64. 162
      docs/zh-Hans/UI/Angular/Migration-Guide-v3.md
  65. 72
      docs/zh-Hans/UI/Angular/Modifying-the-Menu.md
  66. 8
      docs/zh-Hans/UI/Angular/Permission-Management.md
  67. 6
      docs/zh-Hans/UI/Angular/Service-Proxies.md
  68. 205
      docs/zh-Hans/UI/Angular/Subscription-Service.md
  69. 2
      docs/zh-Hans/UI/Angular/Track-By-Service.md
  70. BIN
      docs/zh-Hans/UI/Angular/images/custom-settings.png
  71. BIN
      docs/zh-Hans/UI/Angular/images/generated-files-via-generate-proxy.png
  72. BIN
      docs/zh-Hans/UI/Angular/images/locale-error.png
  73. BIN
      docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png
  74. 3
      docs/zh-Hans/UI/AspNetCore/Dynamic-JavaScript-Proxies.md
  75. 13
      docs/zh-Hans/docs-nav.json
  76. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmApplicationType.cs
  77. 17
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  78. 15
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs
  79. 6
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/ar.json
  80. 2
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
  81. 1
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  82. 11
      framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonModule.cs
  83. 15
      framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerOptions.cs
  84. 25
      framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs
  85. 7
      framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/ar.json
  86. 6
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json
  87. 58
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json
  88. 3
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PeopleAppService_Tests.cs
  89. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json
  90. 222
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock
  91. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json
  92. 236
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock
  93. 58
      framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs
  94. 18
      framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs
  95. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/ar.json
  96. 3
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json
  97. 81
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml
  98. 21
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  99. 37
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml
  100. 141
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

38
.github/lock.yml

@ -0,0 +1,38 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 30
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

15
.github/stale.yml

@ -0,0 +1,15 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Label to use when marking an issue as stale
staleLabel: inactive
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

11
.github/workflows/angular.yml

@ -3,15 +3,6 @@ on:
pull_request:
paths:
- 'npm/ng-packs/**'
branches:
#- master
- dev
push:
paths:
- 'npm/ng-packs/**'
branches:
#- master
- dev
jobs:
build-test-lint:
runs-on: ubuntu-18.04
@ -19,6 +10,6 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '10.x'
node-version: '12.x'
- run: yarn && yarn ci
working-directory: npm/ng-packs

4
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>3.1.0</Version>
<Version>3.1.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io</PackageProjectUrl>

12
docs/en/API/Application-Configuration.md

@ -2,12 +2,12 @@
ABP Framework provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, the list of some fundamental information at this endpoint:
* [Localization](Localization.md) values, supported and the current language of the application.
* Available and granted [policies](Authorization.md) (permissions) for the current user.
* [Setting](Settings.md) values for the current user.
* Info about the [current user](CurrentUser.md) (like id and user name).
* Info about the current [tenant](Multi-Tenancy.md) (like id and name).
* [Time zone](Timing.md) information for the current user and the [clock](Timing.md) type of the application.
* [Localization](../Localization.md) values, supported and the current language of the application.
* Available and granted [policies](../Authorization.md) (permissions) for the current user.
* [Setting](../Settings.md) values for the current user.
* Info about the [current user](../CurrentUser.md) (like id and user name).
* Info about the current [tenant](../Multi-Tenancy.md) (like id and name).
* [Time zone](../Timing.md) information for the current user and the [clock](../Timing.md) type of the application.
## HTTP API

30
docs/en/Authentication/Social-External-Logins.md

@ -0,0 +1,30 @@
# Social/External Logins
## ASP.NET Core MVC / Razor Pages UI
The [Account Module](../Modules/Account.md) has already configured to handle social or external logins out of the box. You can follow the ASP.NET Core documentation to add a social/external login provider to your application.
### Example: Facebook Authentication
Follow the [ASP.NET Core Facebook integration document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to support the Facebook login for your application.
#### Add the NuGet Package
Add the [Microsoft.AspNetCore.Authentication.Facebook](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.Facebook) package to your project. Based on your architecture, this can be `.Web`, `.IdentityServer` (for tiered setup) or `.Host` project.
#### Configure the Provider
Use the `.AddFacebook(...)` extension method in the `ConfigureServices` method of your [module](../Module-Development-Basics.md), to configure the client:
````csharp
context.Services.AddAuthentication()
.AddFacebook(facebook =>
{
facebook.AppId = "...";
facebook.AppSecret = "...";
facebook.Scope.Add("email");
facebook.Scope.Add("public_profile");
});
````
> It would be a better practice to use the `appsettings.json` or the ASP.NET Core User Secrets system to store your credentials, instead of a hard-coded value like that. Follow the [Microsoft's document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to learn the user secrets usage.

2
docs/en/Authorization.md

@ -284,8 +284,6 @@ You may need to check a policy/permission on the client side. For ASP.NET Core M
abp.auth.isGranted('MyPermissionName');
```
See [abp.auth](UI/AspNetCore/JavaScript-API/Index.md) API documentation for details.
## Permission Management
Permission management is normally done by an admin user using the permission management modal:

5
docs/en/Background-Workers-Quartz.md

@ -1,6 +1,6 @@
# Quartz Background Worker Manager
[Quartz](https://www.quartz-scheduler.net/) is an advanced background worker manager. You can integrate Quartz with the ABP Framework to use it instead of the [default background worker manager](Background-Worker.md). ABP simply integrates quartz.
[Quartz](https://www.quartz-scheduler.net/) is an advanced background worker manager. You can integrate Quartz with the ABP Framework to use it instead of the [default background worker manager](Background-Workers.md). ABP simply integrates quartz.
## Installation
@ -36,6 +36,9 @@ public class YourModule : AbpModule
}
````
> Quartz background worker integration provided `QuartzPeriodicBackgroundWorkerAdapter` to adapt `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived class. So, you can still fllow the [background workers document](Background-Workers.md) to define the background worker.
> `BackgroundJobWorker` checks todo jobs every 5 seconds, but quartz will not block when long time jobs are executing. So,after Added Quartz background worker integration, you also need to add [Quartz Background Jobs](Background-Jobs-Quartz.md) or [Hangfire Background Jobs](Background-Jobs-Hangfire.md) to avoid duplicate execution jobs.
## Configuration
See [Configuration](Background-Jobs-Quartz#Configuration).

2
docs/en/Best-Practices/Application-Services.md

@ -206,7 +206,7 @@ This method votes a question and returns the current score of the question.
#### Extra Properties
* **Do** use either `MapExtraPropertiesTo` extension method ([see](Object-Extensions.md)) or configure the object mapper (`MapExtraProperties`) to allow application developers to be able to extend the objects and services.
* **Do** use either `MapExtraPropertiesTo` extension method ([see](../Object-Extensions.md)) or configure the object mapper (`MapExtraProperties`) to allow application developers to be able to extend the objects and services.
#### Manipulating / Deleting Entities

2
docs/en/Blob-Storing-Database.md

@ -27,7 +27,7 @@ This command adds all the NuGet packages to corresponding layers of your solutio
Here, all the NuGet packages defined by this provider;
* [Volo.Abp.BlobStoring.Database.Domain.Shared](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Domain.Shared)
* [Volo.Abp.BlobStoring.Database.Domain.Shared](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.Domain.Shared)
* [Volo.Abp.BlobStoring.Database.Domain](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.Domain)
* [Volo.Abp.BlobStoring.Database.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.EntityFrameworkCore)
* [Volo.Abp.BlobStoring.Database.MongoDB](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.MongoDB)

2
docs/en/Entity-Framework-Core-Migrations.md

@ -2,7 +2,7 @@
This document begins by **introducing the default structure** provided by [the application startup template](Startup-Templates/Application.md) and **discusses various scenarios** you may want to implement for your own application.
> This document is for who want to fully understand and customize the database structure comes with [the application startup template](Startup-Templates/Application.md). If you simply want to create entities and manage your code first migrations, just follow [the startup tutorials](Tutorials/Index.md).
> This document is for who want to fully understand and customize the database structure comes with [the application startup template](Startup-Templates/Application.md). If you simply want to create entities and manage your code first migrations, just follow [the startup tutorials](Tutorials/Part-1.md).
### Source Code

2
docs/en/Modules/Docs.md

@ -613,7 +613,7 @@ Configure<DocsElasticSearchOptions>(options =>
The `Index` is automatically created after the application starts if the `Index` does not exist.
`DefaultElasticClientProvider` is responsible for creating `IElasticClient`. By default, it reads Elastic Search's `Url` from `IConfiguration`.
If your `IElasticClient` needs additional configuration, please use override `IElasticClientProvider` service and replace it in the [dependency injection](Dependency-Injection.md) system.
If your `IElasticClient` needs additional configuration, please use override `IElasticClientProvider` service and replace it in the [dependency injection](../Dependency-Injection.md) system.
```
{

8
docs/en/Startup-Templates/Application.md

@ -437,7 +437,11 @@ See the [Testing Overview](https://reactjs.org/docs/testing.html) document.
* [Formik](https://github.com/jaredpalmer/formik) is used to build forms.
* [Yup](https://github.com/jquense/yup) is used for form validations.
## Social / External Logins
If you want to configure social/external logins for your application, please follow the [Social/External Logins](../Authentication/Social-External-Logins.md) document.
## What's Next?
- [The getting started document](../Getting-Started-With-Startup-Templates.md) explains how to create a new application in a few minutes.
- [The application development tutorial](../Tutorials/Part-1) explains step by step application development.
- [The getting started document](../Getting-Started.md) explains how to create a new application in a few minutes.
- [The application development tutorial](../Tutorials/Part-1.md) explains step by step application development.

2
docs/en/Startup-Templates/Module.md

@ -56,7 +56,7 @@ An `IssueType` enum and an `IssueConsts` class (which may have some constant fie
### .Domain Project
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), value types, [repository interfaces](../Repositories.md) and other domain objects.
An `Issue` entity, an `IssueManager` domain service and an `IIssueRepository` interface are good candidates for this project.

46
docs/en/Tutorials/Part-1.md

@ -233,32 +233,30 @@ namespace Acme.BookStore
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
if (await _bookRepository.GetCountAsync() <= 0)
{
return;
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
}
}

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

@ -80,7 +80,7 @@ This is a typical migration problem and the decision depends on your case;
* You can do it programmatically on data migration or seed phase.
* You can manually handle it on the database.
We prefer to delete the database {{if DB=="EF"}}(run the `Drop-Database` in the *Package Manager Console*){{end}} since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios.
We prefer to **delete the database** {{if DB=="EF"}}(run the `Drop-Database` in the *Package Manager Console*){{end}} since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios.
{{if DB=="EF"}}

87
docs/en/Tutorials/Part-8.md

@ -439,55 +439,66 @@ namespace Acme.BookStore
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
if (await _bookRepository.GetCountAsync() <= 0)
{
return;
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
// ADDED SEED DATA FOR AUTHORS
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
)
);
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
if (await _authorRepository.GetCountAsync() <= 0)
{
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
)
);
}
}
}
}
````
{{if DB=="EF"}}
You can now run the `.DbMigrator` console application to **migrate** the **database schema** and **seed** the initial data.
{{else if DB=="Mongo"}}
You can now run the `.DbMigrator` console application to **seed** the initial data.
{{end}}
## Testing the Author Application Service
Finally, we can write some tests for the `IAuthorAppService`. Add a new class, named `AuthorAppService_Tests` in the `Authors` namespace (folder) of the `Acme.BookStore.Application.Tests` project:

9
docs/en/docs-nav.json

@ -107,6 +107,15 @@
"text": "CLI",
"path": "CLI.md"
},
{
"text": "Authentication",
"items": [
{
"text": "Social/External Logins",
"path": "Authentication/Social-External-Logins.md"
}
]
},
{
"text": "Fundamentals",
"items": [

12
docs/zh-Hans/API/Application-Configuration.md

@ -2,12 +2,12 @@
ABP框架提供了一个预构建的标准端点,其中包含一些有关应用程序/服务的有用信息. 这里是此端点的一些基本信息的列表:
* [本地化](Localization.md)值, 支持应用程序的当前语言.
* 当前用户可用和已授予的[策略](Authorization.md)(权限).
* 当前用户的[设置](Settings.md)值.
* 关于[当前用户](CurrentUser.md)的信息 (如 id 和用户名).
* 关于当前[租户](Multi-Tenancy.md)的信息 (如 id 和名称).
* 当前用户的[时区](Timing.md)信息和应用程序的[时钟](Timing.md)类型.
* [本地化](../Localization.md)值, 支持应用程序的当前语言.
* 当前用户可用和已授予的[策略](../Authorization.md)(权限).
* 当前用户的[设置](../Settings.md)值.
* 关于[当前用户](../CurrentUser.md)的信息 (如 id 和用户名).
* 关于当前[租户](../Multi-Tenancy.md)的信息 (如 id 和名称).
* 当前用户的[时区](../Timing.md)信息和应用程序的[时钟](../Timing.md)类型.
## HTTP API

30
docs/zh-Hans/Authentication/Social-External-Logins.md

@ -0,0 +1,30 @@
# 社交/外部登录
## ASP.NET Core MVC / Razor Pages UI
[帐户模块](../Modules/Account.md)已配置为开箱即用的处理社交或外部登录. 你可以按照ASP.NET Core文档向你的应用程序添加社交/外部登录提供程序.
### 示例: Facebook 认证
按照[ASP.NET Core Facebook集成文档](https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/social/facebook-logins)向你应用程序添加Facebook登录.
#### 添加NuGet包
添加[Microsoft.AspNetCore.Authentication.Facebook]包到你的项目. 基于你的架构,可能是 `.Web`,`.IdentityServer`(对于分层启动)或 `.Host` 项目.
#### 配置提供程序
在你模块的 `ConfigureServices` 方法中使用 `.AddFacebook(...)` 扩展方法来配置客户端:
````csharp
context.Services.AddAuthentication()
.AddFacebook(facebook =>
{
facebook.AppId = "...";
facebook.AppSecret = "...";
facebook.Scope.Add("email");
facebook.Scope.Add("public_profile");
});
````
> 最佳实践是使用 `appsettings.json` 或ASP.NET Core用户机密系统来存储你的凭据,而不是像这样硬编码值. 请参阅[微软](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins)文档了解如何使用用户机密.

2
docs/zh-Hans/Authorization.md

@ -285,8 +285,6 @@ public async Task CreateAsync(CreateAuthorDto input)
abp.auth.isGranted('MyPermissionName');
````
参阅 [abp.auth](UI/AspNetCore/JavaScript-API/Index.md) API 文档了解详情.
## 权限管理
通常权限管理是管理员用户使用权限管理模态框进行授权:

3
docs/zh-Hans/Background-Workers-Quartz.md

@ -36,6 +36,9 @@ public class YourModule : AbpModule
}
````
> Quartz后台工作者集成提供了 `QuartzPeriodicBackgroundWorkerAdapter` 来适配 `PeriodicBackgroundWorkerBase` 和 `AsyncPeriodicBackgroundWorkerBase` 派生类. 所以你依然可以按照[后台工作者文档](Background-Workers.md)来定义后台作业.
> `BackgroundJobWorker` 每5秒检查待执行作业,但是长时间的作业不会阻塞quartz. 所以安装Quartz后台工作者集成后,你同时需要安装[Quartz后台作业](Background-Jobs-Quartz.md)或[Hangfire后台作业](Background-Jobs-Hangfire.md)以避免重复执行作业.
## 配置
参阅[配置](Background-Jobs-Quartz.md#配置).

2
docs/zh-Hans/Best-Practices/Application-Services.md

@ -203,7 +203,7 @@ Task<int> VoteAsync(Guid id, VoteType type);
#### 额外的属性
* **推荐** 使用 `MapExtraPropertiesTo` 扩展方法 ([参阅](Object-Extensions.md)) 或配置对象映射 (`MapExtraProperties`) 以允许应用开发人员能够扩展对象和服务.
* **推荐** 使用 `MapExtraPropertiesTo` 扩展方法 ([参阅](../Object-Extensions.md)) 或配置对象映射 (`MapExtraProperties`) 以允许应用开发人员能够扩展对象和服务.
#### 操作/删除 实体

2
docs/zh-Hans/Blob-Storing-Database.md

@ -27,7 +27,7 @@ abp add-module Volo.Abp.BlobStoring.Database
这里是此提供程序定义的所有包:
* [Volo.Abp.BlobStoring.Database.Domain.Shared](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Domain.Shared)
* [Volo.Abp.BlobStoring.Database.Domain.Shared](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.Domain.Shared)
* [Volo.Abp.BlobStoring.Database.Domain](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.Domain)
* [Volo.Abp.BlobStoring.Database.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.EntityFrameworkCore)
* [Volo.Abp.BlobStoring.Database.MongoDB](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Database.MongoDB)

2
docs/zh-Hans/Entity-Framework-Core-Migrations.md

@ -3,7 +3,7 @@
本文首先介绍[应用程序启动模板](Startup-Templates/Application.md)提供的**默认结构**,并讨论你可能希望为自己的应用程序实现的**各种场景**.
> 本文档适用于希望完全理解和自定义[应用程序启动模板](Startup-Templates/Application.md)附带的数据库结构的人员. 如果你只是想创建实体和管理代码优先(code first)迁移,只需要遵循[启动教程](Tutorials/Index.md).
> 本文档适用于希望完全理解和自定义[应用程序启动模板](Startup-Templates/Application.md)附带的数据库结构的人员. 如果你只是想创建实体和管理代码优先(code first)迁移,只需要遵循[启动教程](Tutorials/Part-1.md).
### 源码

2
docs/zh-Hans/Modules/Docs.md

@ -611,7 +611,7 @@ Configure<DocsElasticSearchOptions>(options =>
应用程序启动后如果`Index`不存在则会自动创建`Index`.
`DefaultElasticClientProvider`负责创建`IElasticClient`, 默认情况下它会从`IConfiguration`中读取Elastic Search的`Url`.
如果你的IElasticClient需要其它配置请使用重写IElasticClientProvider服务并在依赖注入系统中替换它.
如果你的 `IElasticClient` 需要其它配置请使用重写 `IElasticClientProvider` 服务并在[依赖注入](../Dependency-Injection.md)系统中替换它.
```
{
"ElasticSearch": {

8
docs/zh-Hans/Startup-Templates/Application.md

@ -423,7 +423,11 @@ Screens 是通过在 `src/screens` 文件夹中创建将名称分开的文件夹
* [Formik](https://github.com/jaredpalmer/formik) 用于构建表单.
* [Yup](https://github.com/jquense/yup) 用于表单验证.
## 社交/外部登录
如果你想要为你的应用程序配置社交/外部登录,请参阅[社交/外部登录](../Authentication/Social-External-Logins.md)文档.
## 下一步是什么?
* 参阅[ASP.NET Core MVC 模板入门](../Getting-Started-AspNetCore-MVC-Template.md)创建此模板的新解决方案并运行它.
* 参阅[ASP.NET Core MVC 教程](../Tutorials/AspNetCore-Mvc/Part-I.md)学习使用此模板开发应用程序.
* [入门文档](../Getting-Started.md)介绍了如何在几分钟内创建新应用程序.
* [Web应用程序开发教程](../Tutorials/Part-1.md)逐步介绍了应用程序开发.

2
docs/zh-Hans/Startup-Templates/Module.md

@ -56,7 +56,7 @@ abp new Acme.IssueManagement -t module --no-ui
### .Domain 项目
解决方案的领域层. 它主要包含 [实体, 集合根](../Entities.md), [领域服务](../Domain-Services.md), [值类型](../Value-Types.md), [仓储接口](../Repositories.md) 和解决方案的其他领域对象.
解决方案的领域层. 它主要包含 [实体, 集合根](../Entities.md), [领域服务](../Domain-Services.md), 值类型, [仓储接口](../Repositories.md) 和解决方案的其他领域对象.
例如 `Issue` 实体, `IssueManager` 领域服务和 `IIssueRepository` 接口都适合放在这个项目中.

923
docs/zh-Hans/Tutorials/Part-1.md

File diff suppressed because it is too large

1
docs/zh-Hans/Tutorials/Part-10.md

@ -0,0 +1 @@
TODO..

1302
docs/zh-Hans/Tutorials/Part-2.md

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

@ -0,0 +1,255 @@
# Web应用程序开发教程 - 第三章: 集成测试
````json
//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF","Mongo"]
}
````
{{
if UI == "MVC"
UI_Text="mvc"
else if UI == "NG"
UI_Text="angular"
else
UI_Text="?"
end
if DB == "EF"
DB_Text="Entity Framework Core"
else if DB == "Mongo"
DB_Text="MongoDB"
else
DB_Text="?"
end
}}
## 关于本教程
在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的:
* **{{DB_Text}}** 做为ORM提供程序.
* **{{UI_Value}}** 做为UI框架.
本教程分为以下部分:
- [Part 1: 创建服务端](Part-1.md)
- [Part 2: 图书列表页面](Part-2.md)
- [Part 3: 创建,更新和删除图书](Part-3.md)
- **Part 4: 集成测试**(本章)
- [Part 5: 授权](Part-5.md)
- [Part 6: 作者: 领域层](Part-6.md)
- [Part 7: 作者: 数据库集成](Part-7.md)
- [Part 8: 作者: 应用服务层](Part-8.md)
- [Part 9: 作者: 用户页面](Part-9.md)
- [Part 10: 图书到作者的关系](Part-10.md)
## 下载源码
本教程根据你的**UI** 和 **Database**偏好有多个版,我们准备了两种可供下载的源码组合:
* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## 解决方案中的测试项目
这一部分涵盖了 **服务器端** 测试. 解决方案中有多个测试项目:
![bookstore-test-projects-v2](./images/bookstore-test-projects-{{UI_Text}}.png)
每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:
* [xunit](https://xunit.github.io/) 作为主测试框架.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) 作为断言库.
* [NSubstitute](http://nsubstitute.github.io/) 作为模拟库.
{{if DB=="EF"}}
> 测试项目配置为使用 **SQLite内存** 作为数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库.
{{else if DB=="Mongo"}}
> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)**库用于模拟MongoDB数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库.
{{end}}
## 添加测试数据
如果你已经按照[第一部分](Part-1.md)中的描述创建了数据种子贡献者,则相同的数据也在测试中可用. 因此你可以跳过此部分. 如果你尚未创建种子贡献者,可以使用 `BookStoreTestDataSeedContributor` 来为要在以下测试中使用的相同数据提供种子.
## 测试 BookAppService
在 `Acme.BookStore.Application.Tests` 项目中创建一个名叫 `BookAppService_Tests` 的测试类:
````csharp
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit;
namespace Acme.BookStore.Books
{ {{if DB=="Mongo"}}
[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
}
}
}
````
* 测试方法 `Should_Get_List_Of_Books` 直接使用 `BookAppService.GetListAsync` 方法来获取用户列表,并执行检查.
* 我们可以安全地检查 "1984" 这本书的名称,因为我们知道这本书可以在数据库中找到,我们已将其添加到种子数据中.
新增测试方法,用以测试创建一个**合法**book实体的场景:
````csharp
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
````
新增测试方法,用以测试创建一个非法book实体失败的场景:
````csharp
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
````
* 由于 `Name` 是空值, ABP 抛出一个 `AbpValidationException` 异常.
最终的测试类如下所示:
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
namespace Acme.BookStore.Books
{ {{if DB=="Mongo"}}
[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
}
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
}
}
````
打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试:
![bookstore-appservice-tests](./images/bookstore-appservice-tests.png)
恭喜你, **绿色图标**表示测试已成功通过!
## 下一章
查看本教程的[下一章](Part-5.md).

1
docs/zh-Hans/Tutorials/Part-5.md

@ -0,0 +1 @@
TODO..

1
docs/zh-Hans/Tutorials/Part-6.md

@ -0,0 +1 @@
TODO..

1
docs/zh-Hans/Tutorials/Part-7.md

@ -0,0 +1 @@
TODO..

1
docs/zh-Hans/Tutorials/Part-8.md

@ -0,0 +1 @@
TODO..

1
docs/zh-Hans/Tutorials/Part-9.md

@ -0,0 +1 @@
TODO..

BIN
docs/zh-Hans/Tutorials/images/bookstore-add-migration-authors.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-added-author-to-book-list-angular.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-added-author-to-book-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-added-authors-to-modals.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-angular-authors-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-angular-service-proxy-author.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-author-domain-layer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-author-permissions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-authors-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-book-and-booktype.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-book-list-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-dbmigrator-on-solution.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-edit-button-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-edit-delete-actions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-getlist-result-network.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-index-js-file-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-javascript-proxy-console.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-author-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-book-button-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-new-book-button-small.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/zh-Hans/Tutorials/images/bookstore-permissions-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/zh-Hans/Tutorials/images/generated-proxies-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

49
docs/zh-Hans/UI/Angular/Custom-Setting-Page.md

@ -2,38 +2,33 @@
不同的模块提供它们的设置选项卡. 你可以通过3个步骤在项目中自定义设置页面.
1. 创建一个组件
1. 使用以下命令创建一个组件
```js
import { Select } from '@ngxs/store';
import { Component } from '@angular/core';
@Component({
selector: 'app-your-custom-settings',
template: `
custom-settings works!
`,
})
export class YourCustomSettingsComponent {
// Your component logic
}
```bash
yarn ng generate component my-settings
```
2. 添加 `YourCustomSettingsComponent` 到 `AppModule` 中的 `declarations`和 `entryComponents` 数组中.
3. 打开 `app.component.ts` 在 `ngOnInit` 添加以下内容:
2. 打开 `app.component.ts` 做以下修改:
```js
import { addSettingTab } from '@abp/ng.theme.shared';
// ...
ngOnInit() {
addSettingTab({
component: YourCustomSettingsComponent,
name: 'Type here the setting tab title (you can type a localization key, e.g: AbpAccount::Login',
order: 4,
requiredPolicy: 'type here a policy key'
});
import { Component } from '@angular/core';
import { SettingTabsService } from '@abp/ng.core'; // imported SettingTabsService
import { MySettingsComponent } from './my-settings/my-settings.component'; // imported MySettingsComponent
@Component(/* component metadata */)
export class AppComponent {
constructor(private settingTabs: SettingTabsService) // injected MySettingsComponent
{
// added below
settingTabs.add([
{
name: 'MySettings',
order: 1,
requiredPolicy: 'policy key here',
component: MySettingsComponent,
},
]);
}
}
```

47
docs/zh-Hans/UI/Angular/Localization.md

@ -146,16 +146,6 @@ this.store.selectSnapshot(
"build": {
"options": {
"styles": [
{
"input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"inject": true,
@ -166,6 +156,16 @@ this.store.selectSnapshot(
"inject": true,
"bundleName": "fontawesome-v4-shims.min"
},
{
"input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
},
"apps/dev-app/src/styles.scss"
],
}
@ -174,6 +174,7 @@ this.store.selectSnapshot(
}
}
}
```
#### 步骤 2. 清除AppComponent中延迟加载的Fontawesome
@ -192,6 +193,32 @@ import { Component } from '@angular/core';
export class AppComponent {}
```
## 文化名称到语言环境文件名的映射
.NET中定义的某些文化名称与Angular语言环境不匹配. 在这种情况下Angular应用程序在运行时会引发如下错误:
![locale-error](./images/locale-error.png)
如果你看到这样的错误,你应该像下面这样传递 `cultureNameToLocaleFileNameMapping` 属性到CoreModule的forRoot静态方法.
```js
// app.module.ts
@NgModule({
imports: [
// other imports
CoreModule.forRoot({
// other options
cultureNameToLocaleFileNameMapping: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
}
})
//...
```
查看 [Angular中所有的语言环境文件](https://github.com/angular/angular/tree/master/packages/common/locales).
## 另请参阅
* [ASP.NET Core中的本地化](../../Localization.md)

162
docs/zh-Hans/UI/Angular/Migration-Guide-v3.md

@ -18,7 +18,7 @@ yarn ng update @angular/cli @angular/core --force
- 更新你的package.json并安装新的软件包
- 修改tsconfig.json文件创建一个"Solution Style"配置
- 重命名 `browserlist` 为 `.browserlistrc`
- 重命名 `browserslist` 为 `.browserslistrc`
另一方面,如果你单独使用 `yarn ng update` 命令检查首先要更新哪些包会更好. Angular会给你一个要更新的包列表.
@ -144,42 +144,33 @@ export class AppModule {}
AppRoutingModule:
```js
import { DynamicLayoutComponent } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module')
.then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account')
.then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity')
.then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () => import('@abp/ng.tenant-management')
.then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () => import('@abp/ng.setting-management')
.then(m => m.SettingManagementModule.forLazy()),
},
],
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () =>
import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
];
@ -190,7 +181,7 @@ const routes: Routes = [
export class AppRoutingModule {}
```
> 你可能已经注意到我们在top级别路由组件上使用了 `DynamicLayoutComponent`. 我们这样做是为了避免不必要的渲染和闪烁. 这不是强制的,但是我们建议在你的应用程序路由中做同样的事情.
> 你可能已经注意到我们在AppComponent模板中使用了 `<abp-dynamic-layout>` 而不是 `<router-outlet>`. 我们这样做是为了避免不必要的渲染和闪烁. 这不是强制的,但是我们建议在你的应用程序路由中做同样的事情.
#### 如何迁移?
@ -200,7 +191,7 @@ export class AppRoutingModule {}
- 调用 `ThemeBasicModule` 的静态 `forRoot` 方法(或商业上的 `ThemeLeptonModule`),并从导入中删除 `SharedModule`(除非已在其中添加了根模块所需的任何内容).
- 在app路由模块中直接导入延迟ABP模块 (如 `() => import('@abp/ng.identity').then(...)`).
- 在所有延迟模块 `then` 中调用的静态 `forLazy` 方法,即使配置没有被传递.
- [可选]使用 `DynamicLayoutComponent` 添加空的父路由,获得更好的性能和UX.
- [可选]添加 `<abp-dynamic-layout></abp-dynamic-layout>` 到AppComponent模板并且删除 `<router-outlet></router-outlet>`,获得更好的性能和UX.
### RoutesService
@ -233,9 +224,10 @@ export class AppRoutingModule {}
从ABP v3开始,我们已切换到经过严格测试,执行良好的数据表格:[ngx-datatable](https://github.com/swimlane/ngx-datatable). 所有的ABP模块都已经实现了ngx-datatable. `ThemeSharedModule` 已经导出了 `NgxDatatableModule`. 因此如果你在终端运行 `yarn add @swimlane/ngx-datatable` 来安装这个包,它将在你的应用的所有模块中可用.
为了正确设置样式,你需要在angular.json文件的样式部分中添加以下内容:
为了正确设置样式,你需要在angular.json文件的样式部分中添加以下内容(在其他所有元素之上):
```json
"styles": [
{
"input": "node_modules/@swimlane/ngx-datatable/index.css",
"inject": true,
@ -250,7 +242,9 @@ export class AppRoutingModule {}
"input": "node_modules/@swimlane/ngx-datatable/themes/material.css",
"inject": true,
"bundleName": "ngx-datatable-material"
}
},
// other styles
]
```
由于尚未删除 `abp-table`, 因此以前由ABP v2.x构建的模块不会突然丢失所有. 但是它们的外观与内置ABP v3模块有所不同, 因此你可能希望将这些模块中的表转换为ngx-datatable. 为了减少将abp-table转换为ngx-datatable所需的工作量,我们修改了 `ListService` 以使其与 `ngx-datatable` 一起很好地工作,并引入了两个新指令: `NgxDatatableListDirective` 和 `NgxDatatableDefaultDirective`.
@ -348,7 +342,103 @@ export class SomeComponent {
- 如果可以的话,根据上面的例子更新你的模.
- 如果你稍后需要这样做,并且打算保留abp-table一段时间,请确保根据此处描述的[破坏性更改](List-Service.md)更新分页.
**重要说明:**abp-table没有被删除,但已被弃用并在以后的版本中删除. 请考虑切换到ngx-datatable。
**重要说明:**abp-table没有被删除,但已被弃用并在以后的版本中删除. 请考虑切换到ngx-datatable.
### 扩展系统[商业版]
扩展程序系统现在是开源的, 可以从 `@abp/ng.theme.shared/extensions` 而不是从 `@volo/abp.commercial.ng.ui` 中获取. 同样,根据config软件包的新结构,如上所述通过 `forLazy` 静态方法进行配置.
#### 如何迁移?
如果你以前从未使用过扩展系统,则无需执行任何操作. 否则请再次检查文档以查看更改. 扩展系统本身的工作原理与以前相同,唯一的变化是你从中导入的包,静态方法以及您将贡献者传递给的模块.
### Lepton 主题Logo [商业版]
在ABP v2.x中,Lepton每个颜色主题都有一个亮徽标和一个暗徽标. 我们意识到我们可以使它仅使用一个浅色和一个深色徽标. 因此我们更改了Lepton查找徽标图像的方式,现在你只需要在项目中包含 `logo-light.png` 和 `logo-dark.png`.
#### 如何迁移?
如果你之前已切换模板徽标PNG,则更改很简单:
- 转到 `/assets/images/logo` 目录.
- 重命名 `theme1.png` 为 `logo-light.png` 并且重命名 `theme1-reverse.png` 为 `logo-dark.png`.
- 删除所有其他 `theme*.png` 文件.
如果你更换了徽标组件,则更改有些不同,但仍然很简单. `LayoutStateService` 有两个新成员: `primaryLogoColor` 和 `secondaryLogoColor`. 它们有 `'light'` 和 `'dark'` 设置值做为可观察流. 你可以使用 `async` 管道在自定义徽标组件模板中使用它们的值. 这是一个完整的示例,其中涵盖了主要和辅助(帐户)布局徽标.
```js
import { AddReplaceableComponent } from '@abp/ng.core';
import { CommonModule } from '@angular/common';
import { APP_INITIALIZER, Component, Injector, NgModule } from '@angular/core';
import { Store } from '@ngxs/store';
import { eAccountComponents } from '@volo/abp.ng.account';
import {
AccountLayoutComponent,
eThemeLeptonComponents,
LayoutStateService,
} from '@volo/abp.ng.theme.lepton';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({
template: `
<div class="account-brand p-4 text-center mb-1" *ngIf="isAccount; else link">
<ng-template [ngTemplateOutlet]="link"></ng-template>
</div>
<ng-template #link>
<a [style.background-image]="logoUrl | async" class="navbar-brand" routerLink="/"></a>
</ng-template>
`,
})
export class LogoComponent {
isAccount: boolean;
logoColor: Observable<'dark' | 'light'>;
get logoUrl() {
return this.logoColor.pipe(map(color => `url(/assets/images/logo/logo-${color}.png)`));
}
constructor(injector: Injector) {
const layout = injector.get(LayoutStateService);
this.isAccount = Boolean(injector.get(AccountLayoutComponent, false));
this.logoColor = this.isAccount ? layout.secondaryLogoColor : layout.primaryLogoColor;
}
}
@NgModule({
imports: [CommonModule],
declarations: [LogoComponent],
exports: [LogoComponent],
})
export class LogoModule {}
export const APP_LOGO_PROVIDER = [
{ provide: APP_INITIALIZER, useFactory: switchLogos, multi: true, deps: [Store] },
];
export function switchLogos(store: Store) {
return () => {
store.dispatch(
new AddReplaceableComponent({
component: LogoComponent,
key: eThemeLeptonComponents.Logo,
}),
);
store.dispatch(
new AddReplaceableComponent({
component: LogoComponent,
key: eAccountComponents.Logo,
}),
);
};
}
```
只要将 `APP_LOGO_PROVIDER` 添加到根模块的提供程序(通常是 `AppModule` ),你就会有一个调整主题颜色的自定义徽标组件.
### 过时的接口

72
docs/zh-Hans/UI/Angular/Modifying-the-Menu.md

@ -2,8 +2,6 @@
菜单在 @abp/ng.theme.basic包 `ApplicationLayoutComponent` 内部. 有几种修改菜单的方法,本文档介绍了这些方法. 如果你想完全替换菜单,请参考[组件替换文档]了解如何替换布局.
<!-- TODO: Replace layout replacement document with component replacement. Layout replacement document will be created.-->
## 如何添加Logo
环境变量中的 `logoUrl` 是logo的url.
@ -212,51 +210,65 @@ this.routes.remove(['Your navigation']);
## 如何在菜单的右侧添加元素
右侧的元素存储在 @abp/ng.theme.basic 包的 `LayoutState` 中.
`LayoutStateService` 的 `dispatchAddNavigationElement` 方法添加元素到右侧的菜单.
你可以通过将模板添加到 `app.component` 调用 `dispatchAddNavigationElement` 方法来插入元素:
你可以通过调用 `NavItemsService` 的 `addItems` 方法将元素添加到菜单的右侧. 这是一个单例服务,即以根身份提供. 因此你可以立即注入并使用它.
```js
import { Layout, LayoutStateService } from '@abp/ng.theme.basic'; // added this line
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<!-- Added below content -->
<ng-template #search
><input type="search" placeholder="Search" class="bg-transparent border-0"
/></ng-template>
<input type="search" placeholder="Search" class="bg-transparent border-0 color-white" />
`,
})
export class AppComponent {
// Added ViewChild
@ViewChild('search', { static: false, read: TemplateRef }) searchElementRef: TemplateRef<any>;
constructor(private layout: LayoutStateService) {} // injected LayoutStateService
export class MySearchInputComponent {}
// Added ngAfterViewInit
ngAfterViewInit() {
const newElement = {
name: 'Search',
element: this.searchElementRef,
order: 1,
} as Layout.NavigationElement;
this.layout.dispatchAddNavigationElement(newElement);
@Component(/* component metadata */)
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.addItems([
{
id: 'MySearchInput',
order: 1,
component: MySearchInputComponent,
},
{
id: 'SignOutIcon',
html: '<i class="fas fa-sign-out-alt fa-lg text-white m-2"><i>',
action: () => console.log('Clicked the sign out icon'),
order: 101, // puts as last element
},
]);
}
}
```
上面我们在菜单添加了一个搜索输入,最终UI如下:
上面我们在菜单添加了一个搜索输入和退出登录图标,最终UI如下:
![navigation-menu-search-input](./images/navigation-menu-search-input.png)
## 如何删除右侧菜单元素
> 默认元素的排序为 `100`. 如果要将自定义元素放在默认值之前,请指定一个排序,最高为 `99`. 如果要将自定义元素放在默认值之后,请指定排序从 `101` 开始. 最后如果必须在默认值之间放置一个项目,请按如下所述修补默认元素顺序. 但有一个警告:我们将来可能会添加另一个默认元素,排序也为 `100`.
## 如何修补或删除右侧部分元素
`NavItemsService` 的 `patchItem` 方法通过 `id` 查找元素,并将配置替换为第二个参数传递的新配置. `removeItem` 方法会找到一个元素并删除.
```js
export class AppComponent {
constructor(private navItems: NavItemsService) {
navItems.patchItem(eThemeBasicComponents.Languages, {
requiredPolicy: 'new policy here',
order: 1,
});
navItems.removeItem(eThemeBasicComponents.CurrentUser);
}
}
```
TODO
* 使用新的 `requiredPolicy` 和新的 `order` 修补了语言下拉菜单元素.
* 删除了当前用户的下拉菜单元素.
## 下一步是什么?

8
docs/zh-Hans/UI/Angular/Permission-Management.md

@ -55,7 +55,9 @@ export class YourComponent {
如果你想要在导航过程中控制经过身份验证的用户对路由的访问权限,可以使用 `PermissionGuard`.
将 `requiredPolicy` 添加到路由模块中的 `routes`属性.
* 从@abp/ng.core导入PermissionGuard.
* 添加 `canActivate: [PermissionGuard]` 到你的路由对象.
* 添加 `requiredPolicy` 到路由模块路由的 `data` 属性.
```js
import { PermissionGuard } from '@abp/ng.core';
@ -66,9 +68,7 @@ const routes: Routes = [
component: YourComponent,
canActivate: [PermissionGuard],
data: {
routes: {
requiredPolicy: 'AbpIdentity.Roles.Create',
},
requiredPolicy: 'YourProjectName.YourComponent', // policy key for your component
},
},
];

6
docs/zh-Hans/UI/Angular/Service-Proxies.md

@ -33,7 +33,7 @@ abp generate-proxy
服务的 `providerIn` 属性定义为 `'root'`. 因此无需将服务作为提供程序添加到模块. 你可以通过将服务注入到构造函数中来使用它,如下所示:
```js
import { AbpApplicationConfigurationService } from '../app/shared/services';
import { AbpApplicationConfigurationService } from '../abp/applicationconfiguration/services';
//...
export class HomeComponent{
@ -49,14 +49,14 @@ Angular编译器会从最终输出中删除那些没有被注入的服务. 参
### Models
生成的模型与后端中的dto匹配. 每个模型在 `src/app/*/shared/models` 文件夹生成一个类.
生成的模型与后端中的dto匹配. 每个模型在 `src/app/*/models` 文件夹生成一个类.
`@abp/ng.core` 包有一些[基类](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/dtos.ts). 一些模型扩展了这些类.
可以如下所示创建一个类的实例:
```js
import { IdentityRoleCreateDto } from '../identity/shared/models';
import { IdentityRoleCreateDto } from '../identity/role/models'
//...
const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, isPublic: true})
```

205
docs/zh-Hans/UI/Angular/Subscription-Service.md

@ -0,0 +1,205 @@
# 轻松取消将你的观察取消订阅
`SubscriptionService` 是一个实用工具服务,它提供了一个简单的取消订阅Angular组件和指令中的RxJS可观察对象的功能. 请参见[为什么在实例销毁时要取消订阅可观察对象](https://angular.io/guide/lifecycle-hooks#cleaning-up-on-instance-destruction).
## 入门
你必须在组件或指令级别提供 `SubscriptionService`,因为它没有在**根中提供**,而且它与组件/指令的生命周期同步. 只有在此之后,您才能注入并开始使用它。
```js
import { SubscriptionService } from '@abp/ng.core';
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent {
count$ = interval(1000);
constructor(private subscription: SubscriptionService) {
this.subscription.addOne(this.count$, console.log);
}
}
```
由 `count$` 发出的值将被记录下来,直到组件被销毁. 你不必手动退订.
> 请不要尝试使用单例 `SubscriptionService`. 这是行不通的.
## 用法
## 如何订阅可观察对象
你可以传递 `next` 函数和 `error` 函数.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
const source$ = interval(1000);
const nextFn = value => console.log(value * 2);
const errorFn = error => {
console.error(error);
return of(null);
};
this.subscription.addOne(source$, nextFn, errorFn);
}
}
```
或者,你可以传递一个观察者.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
const source$ = interval(1000);
const observer = {
next: value => console.log(value * 2),
complete: () => console.log('DONE'),
};
this.subscription.addOne(source$, observer);
}
}
```
`addOne` 方法返回单个订阅以便你以后使用它. 有关详细信息,请参见下面的主题.
### 实例销毁之前如何退订
有两种方法可以做到这一点. 如果你不想再次订阅.可以使用 `closeAll` 方法.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);
}
onSomeEvent() {
this.subscription.closeAll();
}
}
```
这将清除所有订阅,你将无法再次订阅. 如果你打算添加另一个订阅,可以使用`reset`方法.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);
}
onSomeEvent() {
this.subscription.reset();
this.subscription.addOne(interval(1000), console.warn);
}
}
```
### 如何取消单个订阅
有时你可能需要取消订阅特定的订阅,但保留其他订阅. 在这种情况下,你可以使用 `closeOne` 方法.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
countSubscription: Subscription;
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
this.countSubscription = this.subscription.addOne(
interval(1000),
console.log
);
}
onSomeEvent() {
this.subscription.closeOne(this.countSubscription);
console.log(this.countSubscription.closed); // true
}
}
```
### 如何从跟踪的订阅中删除单个订阅
你可能需要控制特定的订阅. 在这种情况下你可以使用 `removeOne` 方法将其从跟踪的订阅中删除.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
countSubscription: Subscription;
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
this.countSubscription = this.subscription.addOne(
interval(1000),
console.log
);
}
onSomeEvent() {
this.subscription.removeOne(this.countSubscription);
console.log(this.countSubscription.closed); // false
}
}
```
### 如何检查是否全部取消订阅
使用 `isClosed` 检查 `closeAll` 是否被调用.
```js
@Component({
/* class metadata here */
providers: [SubscriptionService],
})
class DemoComponent implements OnInit {
constructor(private subscription: SubscriptionService) {}
ngOnInit() {
this.subscription.addOne(interval(1000), console.log);
}
onSomeEvent() {
console.log(this.subscription.isClosed); // false
}
}
```
## 下一步是什么?
- [ListService](./List-Service.md)

2
docs/zh-Hans/UI/Angular/Track-By-Service.md

@ -99,4 +99,4 @@ class DemoComponent {
## 下一步是什么?
- [ListService](./List-Service.md)
- [SubscriptionService](./Subscription-Service.md)

BIN
docs/zh-Hans/UI/Angular/images/custom-settings.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 106 KiB

BIN
docs/zh-Hans/UI/Angular/images/generated-files-via-generate-proxy.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/zh-Hans/UI/Angular/images/locale-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/zh-Hans/UI/Angular/images/navigation-menu-search-input.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

3
docs/zh-Hans/UI/AspNetCore/Dynamic-JavaScript-Proxies.md

@ -0,0 +1,3 @@
# Dynamic JavaScript HTTP API Proxies
TODO

13
docs/zh-Hans/docs-nav.json

@ -80,6 +80,15 @@
"text": "CLI",
"path": "CLI.md"
},
{
"text": "认证",
"items": [
{
"text": "社交/外部登录",
"path": "Authentication/Social-External-Logins.md"
}
]
},
{
"text": "基础知识",
"items": [
@ -440,6 +449,10 @@
"text": "TrackByService",
"path": "UI/Angular/Track-By-Service.md"
},
{
"text": "SubscriptionService",
"path": "UI/Angular/Subscription-Service.md"
},
{
"text": "ListService",
"path": "UI/Angular/List-Service.md"

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmApplicationType.cs

@ -5,6 +5,7 @@ namespace Volo.Abp.Cli.ProjectModification
[Flags]
public enum NpmApplicationType
{
Mvc = 1
Mvc = 1,
Angular = 2
}
}
}

17
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -39,7 +39,7 @@ namespace Volo.Abp.Cli.ProjectModification
EfCoreMigrationAdder efCoreMigrationAdder,
DerivedClassFinder derivedClassFinder,
ProjectNpmPackageAdder projectNpmPackageAdder,
NpmGlobalPackagesChecker npmGlobalPackagesChecker,
NpmGlobalPackagesChecker npmGlobalPackagesChecker,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler,
SourceCodeDownloadService sourceCodeDownloadService,
SolutionFileModifier solutionFileModifier,
@ -159,7 +159,7 @@ namespace Volo.Abp.Cli.ProjectModification
{
Directory.Delete(demoFolder, true);
}
var hostFolder = Path.Combine(targetModuleFolder, "host");
if (Directory.Exists(hostFolder))
{
@ -186,7 +186,9 @@ namespace Volo.Abp.Cli.ProjectModification
await ProjectNugetPackageAdder.AddAsync(targetProjectFile, nugetPackage);
}
if (!module.NpmPackages.IsNullOrEmpty())
var mvcNpmPackages = module.NpmPackages?.Where(p => p.ApplicationType.HasFlag(NpmApplicationType.Mvc)).ToList();
if (!mvcNpmPackages.IsNullOrEmpty())
{
var targetProjects = ProjectFinder.FindNpmTargetProjectFile(projectFiles);
if (targetProjects.Any())
@ -195,8 +197,7 @@ namespace Volo.Abp.Cli.ProjectModification
foreach (var targetProject in targetProjects)
{
foreach (var npmPackage in module.NpmPackages.Where(p =>
p.ApplicationType.HasFlag(NpmApplicationType.Mvc)))
foreach (var npmPackage in mvcNpmPackages)
{
await ProjectNpmPackageAdder.AddAsync(Path.GetDirectoryName(targetProject), npmPackage);
}
@ -222,7 +223,7 @@ namespace Volo.Abp.Cli.ProjectModification
}
var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj"));
if (dbMigrationsProject == null)
{
Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project.");
@ -241,7 +242,7 @@ namespace Volo.Abp.Cli.ProjectModification
if (addedNewBuilder && !skipDbMigrations)
{
EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
}
}
@ -274,4 +275,4 @@ namespace Volo.Abp.Cli.ProjectModification
.Any(p => p.EndsWith(".IdentityServer") || p.EndsWith(".HttpApi.Host"));
}
}
}
}

15
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs

@ -9,6 +9,14 @@ namespace System.Collections.Generic
/// </summary>
public static class AbpListExtensions
{
public static void InsertRange<T>(this IList<T> source, int index, IEnumerable<T> items)
{
foreach (var item in items)
{
source.Insert(index++, item);
}
}
public static int FindIndex<T>(this IList<T> source, Predicate<T> selector)
{
for (var i = 0; i < source.Count; ++i)
@ -182,7 +190,7 @@ namespace System.Collections.Generic
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <returns>
/// Returns a new list ordered by dependencies.
/// If A depends on B, then B will come before than A in the resulting list.
/// If A depends on B, then B will come before than A in the resulting list.
/// </returns>
public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
{
@ -202,14 +210,15 @@ namespace System.Collections.Generic
}
/// <summary>
///
///
/// </summary>
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="item">Item to resolve</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <param name="sorted">List with the sortet items</param>
/// <param name="visited">Dictionary with the visited items</param>
private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted,
Dictionary<T, bool> visited)
{
bool inProcess;
var alreadyVisited = visited.TryGetValue(item, out inProcess);

6
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/ar.json

@ -1,4 +1,4 @@
{
{
"culture": "ar",
"texts": {
"DisplayName:Abp.Mailing.DefaultFromAddress": "العنوان الإفتراضي",
@ -18,6 +18,8 @@
"Description:Abp.Mailing.Smtp.Password": "كلمة المرور لاسم المستخدم المرتبط ببيانات الاعتماد.",
"Description:Abp.Mailing.Smtp.Domain": "اسم المجال أو الكمبيوتر الذي يتحقق من بيانات الاعتماد.",
"Description:Abp.Mailing.Smtp.EnableSsl": "ما إذا كان SmtpClient يستخدم (SSL) لتشفير الاتصال.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "إرسال الصلاحيات الافتراضية مع الطلب."
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "إرسال الصلاحيات الافتراضية مع الطلب.",
"TextTemplate:StandardEmailTemplates.Layout": "نموذج تخطيط إفتراضي للبريد الإلكتروني",
"TextTemplate:StandardEmailTemplates.Message": "نموذج بسيط للرسائل الإلكترونية"
}
}

2
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs

@ -242,7 +242,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory
}
}
if (IsBaseAuditProperty(propertyInfo, entityType))
if (propertyInfo != null && IsBaseAuditProperty(propertyInfo, entityType))
{
return false;
}

1
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -17,7 +17,6 @@ using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.ProxyScripting.Generators;
using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Reflection;
using Volo.Abp.Threading;
using Volo.Abp.Tracing;

11
framework/src/Volo.Abp.Json/Volo/Abp/Json/AbpJsonModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.Modularity;
using Volo.Abp.Json.Newtonsoft;
using Volo.Abp.Modularity;
using Volo.Abp.Timing;
namespace Volo.Abp.Json
@ -6,6 +7,12 @@ namespace Volo.Abp.Json
[DependsOn(typeof(AbpTimingModule))]
public class AbpJsonModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{
options.Converters.Add<AbpJsonIsoDateTimeConverter>();
});
}
}
}

15
framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/AbpNewtonsoftJsonSerializerOptions.cs

@ -0,0 +1,15 @@
using Newtonsoft.Json;
using Volo.Abp.Collections;
namespace Volo.Abp.Json.Newtonsoft
{
public class AbpNewtonsoftJsonSerializerOptions
{
public ITypeList<JsonConverter> Converters { get; }
public AbpNewtonsoftJsonSerializerOptions()
{
Converters = new TypeList<JsonConverter>();
}
}
}

25
framework/src/Volo.Abp.Json/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs

@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Volo.Abp.DependencyInjection;
@ -7,14 +11,19 @@ namespace Volo.Abp.Json.Newtonsoft
{
public class NewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency
{
private readonly AbpJsonIsoDateTimeConverter _dateTimeConverter;
private static readonly CamelCaseExceptDictionaryKeysResolver SharedCamelCaseExceptDictionaryKeysResolver =
new CamelCaseExceptDictionaryKeysResolver();
public NewtonsoftJsonSerializer(AbpJsonIsoDateTimeConverter dateTimeConverter)
protected List<JsonConverter> Converters { get; }
public NewtonsoftJsonSerializer(
IOptions<AbpNewtonsoftJsonSerializerOptions> options,
IServiceProvider serviceProvider)
{
_dateTimeConverter = dateTimeConverter;
Converters = options.Value
.Converters
.Select(c => (JsonConverter) serviceProvider.GetRequiredService(c))
.ToList();
}
public string Serialize(object obj, bool camelCase = true, bool indented = false)
@ -36,8 +45,8 @@ namespace Volo.Abp.Json.Newtonsoft
{
var settings = new JsonSerializerSettings();
settings.Converters.Insert(0, _dateTimeConverter);
settings.Converters.InsertRange(0, Converters);
if (camelCase)
{
settings.ContractResolver = SharedCamelCaseExceptDictionaryKeysResolver;
@ -47,7 +56,7 @@ namespace Volo.Abp.Json.Newtonsoft
{
settings.Formatting = Formatting.Indented;
}
return settings;
}
@ -63,4 +72,4 @@ namespace Volo.Abp.Json.Newtonsoft
}
}
}
}
}

7
framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"DisplayName:Abp.Timing.Timezone": "المنطقة زمنية",
"Description:Abp.Timing.Timezone": "المنطقة الزمنية للتطبيق"
}
}

6
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json

@ -1,4 +1,4 @@
{
{
"culture": "ar",
"texts": {
"InternalServerErrorMessage": "حدث خطأ داخلي أثناء طلبك!",
@ -47,6 +47,8 @@
"PagerInfoEmpty": "عرض 0 الي 0 من 0 إدخالات",
"PagerInfoFiltered": "(تمت تصفيته من _MAX_ مجموع الإدخالات)",
"NoDataAvailableInDatatable": "لا توجد بيانات متاحة في الجدول",
"Total": "مجموع",
"Selected": "مختارة",
"PagerShowMenuEntries": "عرض _MENU_ إدخالات",
"DatatableActionDropdownDefaultText": "أجراءات",
"ChangePassword": "تغيير كلمة السر",
@ -61,4 +63,4 @@
"GoBack": "عد",
"Search": "بحث"
}
}
}

58
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json

@ -1,34 +1,34 @@
{
"culture": "ar",
"texts": {
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.",
"The {0} field is not a valid credit card number.": "The {0} field is not a valid credit card number.",
"{0} is not valid.": "{0} is not valid.",
"The {0} field is not a valid e-mail address.": "The {0} field is not a valid e-mail address.",
"The {0} field only accepts files with the following extensions: {1}": "The {0} field only accepts files with the following extensions: {1}",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "The field {0} must be a string or array type with a maximum length of '{1}'.",
"The field {0} must be a string or array type with a minimum length of '{1}'.": "The field {0} must be a string or array type with a minimum length of '{1}'.",
"The {0} field is not a valid phone number.": "The {0} field is not a valid phone number.",
"The field {0} must be between {1} and {2}.": "The field {0} must be between {1} and {2}.",
"The field {0} must match the regular expression '{1}'.": "The field {0} must match the regular expression '{1}'.",
"The {0} field is required.": "The {0} field is required.",
"The field {0} must be a string with a maximum length of {1}.": "The field {0} must be a string with a maximum length of {1}.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "The {0} field is not a valid fully-qualified http, https, or ftp URL.",
"The field {0} is invalid.": "The field {0} is invalid.",
"ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.",
"ThisFieldIsNotValid.": "This field is not valid.",
"ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.",
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "This field only accepts files with the following extensions: {0}",
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthoOf{0}": "This field must be a string or array type with a maximum length of '{0}'.",
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "This field must be a string or array type with a minimum length of '{0}'.",
"ThisFieldIsNotAValidPhoneNumber.": "This field is not a valid phone number.",
"ThisFieldMustBeBetween{0}And{1}": "This field must be between {0} and {1}.",
"ThisFieldMustMatchTheRegularExpression{0}": "This field must match the regular expression '{0}'.",
"ThisFieldIsRequired.": "This field is required.",
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "This field must be a string with a maximum length of {0}.",
"ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "This field must be a string with a minimum length of {1} and a maximum length of {0}.",
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "This field is not a valid fully-qualified http, https, or ftp URL.",
"ThisFieldIsInvalid.": "This field is invalid."
"'{0}' and '{1}' do not match.": "'{0}' و '{1}' غير متطابقان",
"The {0} field is not a valid credit card number.": "الحقل {0} لا يمثل رقم بطاقة ائتمان صالح.",
"{0} is not valid.": "{0} غير صالح.",
"The {0} field is not a valid e-mail address.": "الحقل {0} لا يمثل عنوان بريد إلكتروني صالح.",
"The {0} field only accepts files with the following extensions: {1}": "الحقل {0} يقبل الملفات ذات الامتدادات التالية فقط: {1}",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "يجب أن يكون الحقل {0} عبارة عن سلسلة أو صفيف طوله '{1}' كحد أقصى.",
"The field {0} must be a string or array type with a minimum length of '{1}'.": "يجب أن يكون الحقل {0} عبارة عن سلسلة أو صفيف طوله كحد أدنى '{1}'.",
"The {0} field is not a valid phone number.": "الحقل {0} لا يمثل رقم هاتف صالح.",
"The field {0} must be between {1} and {2}.": "يجب أن يكون الحقل {0} بين {1} و {2}.",
"The field {0} must match the regular expression '{1}'.": "يجب أن يتطابق الحقل {0} مع التعبير النمطي '{1}'.",
"The {0} field is required.": "الحقل {0} إجباري.",
"The field {0} must be a string with a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها كحد أقصى {1}.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها {1} كحد أدنى و {2} كحد أقصى.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "الحقل {0} ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp",
"The field {0} is invalid.": "الحقل {0} غير صالح.",
"ThisFieldIsNotAValidCreditCardNumber.": "هذا الحقل لا يمثل رقم بطاقة ائتمان صالح.",
"ThisFieldIsNotValid.": "هذا الحقل غير صالح.",
"ThisFieldIsNotAValidEmailAddress.": "هذا الحقل لا يمثل عنوان بريد إلكتروني صالح.",
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "هذا الحقل يقبل الملفات ذات الامتدادات التالية فقط: {0}",
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف أو صفيف لا يتعدى طوله '{0}'.",
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": " هذا الحقل يجب أن يكون عبارة عن سلسلة أو صفيف طوله كحد أدنى '{0}'.",
"ThisFieldIsNotAValidPhoneNumber.": "هذا الحقل لا يمثل رقم هاتف صالح.",
"ThisFieldMustBeBetween{0}And{1}": "هذا الحقل يجب أن يكون بين {0} و {1}.",
"ThisFieldMustMatchTheRegularExpression{0}": "هذا الحقل يجب أن يتطابق مع التعبير النمطي '{0}'.",
"ThisFieldIsRequired.": "هذا الحقل إجباري.",
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف طولها كحد أقصى {0}.",
"ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف طولها {0} كحد أدنى و {1} كحد أقصى.",
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "هذا الحقل ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp",
"ThisFieldIsInvalid.": "هذا الحقل غير صالح."
}
}

3
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PeopleAppService_Tests.cs

@ -83,7 +83,6 @@ namespace Volo.Abp.AspNetCore.Mvc
(await _personRepository.FindAsync(resultDto.Id)).ShouldNotBeNull();
}
[Fact]
public async Task Update_Test()
{
@ -170,4 +169,4 @@ namespace Volo.Abp.AspNetCore.Mvc
douglas.Phones.Any(p => p.Number == firstPhone.Number).ShouldBeFalse();
}
}
}
}

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json

@ -3,7 +3,7 @@
"name": "asp.net",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.shared": "^3.0.4",
"@abp/aspnetcore.mvc.ui.theme.shared": "^3.0.5",
"highlight.js": "^9.13.1"
},
"devDependencies": {}

222
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock

@ -2,30 +2,30 @@
# yarn lockfile v1
"@abp/aspnetcore.mvc.ui.theme.shared@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-3.0.4.tgz#50db612b6492d36c63dd9042000a9fb00fecec54"
integrity sha512-imd2T5XXkNvGQyXV/A9hA80qSeurKaUcW6HDkte8Y2fs4QfvkjgzWXi7neqF6F+HVbYYPg3rIBWC6QF1OXGn2w==
dependencies:
"@abp/aspnetcore.mvc.ui" "~3.0.4"
"@abp/bootstrap" "~3.0.4"
"@abp/bootstrap-datepicker" "~3.0.4"
"@abp/datatables.net-bs4" "~3.0.4"
"@abp/font-awesome" "~3.0.4"
"@abp/jquery-form" "~3.0.4"
"@abp/jquery-validation-unobtrusive" "~3.0.4"
"@abp/lodash" "~3.0.4"
"@abp/luxon" "~3.0.4"
"@abp/malihu-custom-scrollbar-plugin" "~3.0.4"
"@abp/select2" "~3.0.4"
"@abp/sweetalert" "~3.0.4"
"@abp/timeago" "~3.0.4"
"@abp/toastr" "~3.0.4"
"@abp/aspnetcore.mvc.ui@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-3.0.4.tgz#4e4344d95a98a60c53037b351cb915d8ff7a7dcb"
integrity sha512-p7KI5CKkpD4EYzG4fCtygrtZhwMu7Z+IZFGbdqXelLbI5ACkbJyrn+N+9trPfS9papymeXY4p1E3v6xOH7rSHQ==
"@abp/aspnetcore.mvc.ui.theme.shared@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-3.0.5.tgz#5b370902cf229963f9aa10dd64482d57299dde76"
integrity sha512-iobAH39xrCdnqdaABaiYZg4lqNSOwPkMCLnyc+ytxOMbu/YnNKahXPmRhgd8dR/l+iQY1+6T4SWfLdGswi3gAg==
dependencies:
"@abp/aspnetcore.mvc.ui" "~3.0.5"
"@abp/bootstrap" "~3.0.5"
"@abp/bootstrap-datepicker" "~3.0.5"
"@abp/datatables.net-bs4" "~3.0.5"
"@abp/font-awesome" "~3.0.5"
"@abp/jquery-form" "~3.0.5"
"@abp/jquery-validation-unobtrusive" "~3.0.5"
"@abp/lodash" "~3.0.5"
"@abp/luxon" "~3.0.5"
"@abp/malihu-custom-scrollbar-plugin" "~3.0.5"
"@abp/select2" "~3.0.5"
"@abp/sweetalert" "~3.0.5"
"@abp/timeago" "~3.0.5"
"@abp/toastr" "~3.0.5"
"@abp/aspnetcore.mvc.ui@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-3.0.5.tgz#a050eaee328c56a3730f5abbf25035adf612d6f0"
integrity sha512-/EOgjXTzR+I1OK9KA7L3yzQ2RGSDW97+n3cQrSdLAUF+tnRvMJS0nytDlSJSRK2hb8/clFMaofZHCC81aUkYUg==
dependencies:
ansi-colors "^4.1.1"
extend-object "^1.0.0"
@ -34,144 +34,144 @@
path "^0.12.7"
rimraf "^3.0.2"
"@abp/bootstrap-datepicker@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-3.0.4.tgz#d21963f158a4d348a0acdc46209e67378aae8e55"
integrity sha512-25e9qm39dj9AijIchQIdLlBiBM5/fhASkEuLKNt6M8S/e3qwKLwbRhX90DKMFvOyKdxxcK1gX7tDuGHyG9A8mA==
"@abp/bootstrap-datepicker@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-3.0.5.tgz#3a7fd86dbcadc31c11be5b2b31c8cf8ac3035540"
integrity sha512-vIY1wT4RHoJAPhVK11Q5MS71iZbwsmM/0VTBvvs04mjRSSi6xhdyRYkA+l+P49fnOwhZNaaK19XwQdVVScDuhA==
dependencies:
bootstrap-datepicker "^1.9.0"
"@abp/bootstrap@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-3.0.4.tgz#3b0e2b74dc174a04ce669de1b140df833eee6031"
integrity sha512-DwxL1fGYOwChPwmMpwUxCKiIdzTfRf/YDkOLUiwI8eRrpABBeuE2sSHKfks6Rqh9Wqo/nlWXvfccgDRqweqhsA==
"@abp/bootstrap@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-3.0.5.tgz#91f2c8fe6a57fc5789ae8fe9c206b2c159e55880"
integrity sha512-LdydeJIbw5k1k7FkWaI1lTectZeTXmEIoccJtj9Qx4N9yGt40wGsjz9SOvUdJ0IKAgntIWNQP8ohWiBPgzT7Xg==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
bootstrap "^4.5.0"
bootstrap-v4-rtl "4.4.1-2"
"@abp/core@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-3.0.4.tgz#281940525e280e9e3a1aa4cb4e8f64548ea026a3"
integrity sha512-ZPEH1fa5bf/f1ek9ZLQHx7u78iF6kRZ8Z2NBI36cNL13RyQbx485qJF+KXyqNlJoJAh0CAs0NsAJADU3utcDNA==
"@abp/core@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-3.0.5.tgz#9a7bd990d02edc8128a5be3233b6fbb5669b15b8"
integrity sha512-LdkSkZQfN4hugcGNxBkje0+eQomivwNFbnAkMFQbvCwYLwKB2yvk7GCemEetW1piqvyrUGz4jqhtunjoXps5JA==
dependencies:
"@abp/utils" "^3.0.4"
"@abp/utils" "^3.0.5"
"@abp/datatables.net-bs4@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-3.0.4.tgz#e488336bd2c199146ea60464e0ba75aeb134957e"
integrity sha512-ID1XWKPxG9pv6mxBkku7JWvgXjTxJ1uShxmYv5kFYVuwOGMSvj8QVZInTHFcMdECopV56yjGPNOIFLeQj4Mw8Q==
"@abp/datatables.net-bs4@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-3.0.5.tgz#9b79935970d2d4074994240b5deb01dff5987c0a"
integrity sha512-jIWRBEG/NlhaE43i6Xzr18XJ5aTWA+WWslf7WgNOArG5slGBbH4lLLja4HswLP3NGdSmzIyHJR1p9XQ82nBkbQ==
dependencies:
"@abp/datatables.net" "~3.0.4"
"@abp/datatables.net" "~3.0.5"
datatables.net-bs4 "^1.10.21"
"@abp/datatables.net@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-3.0.4.tgz#7ef143b83143ad8d769be25296411a812a17b255"
integrity sha512-gdQwRuxO5fQWhG/DiUaf40DQ8dWIVGhOCyt6SZ8LQzq4uSaQqa0qrtTTTW2/aiee4MgJu2vJWYF5YCszIR/HhA==
"@abp/datatables.net@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-3.0.5.tgz#0a0a39e8fa54522bf8a09ce76ff3927207a79491"
integrity sha512-HClQ2tcZuWBAChf9oOJOtMP1zHv8jqloPEcw3iqbhS/tWZBw5VQ4TO+BVfZsqHDXZNnWSmR6/2xiKsK32nliew==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
datatables.net "^1.10.21"
"@abp/font-awesome@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-3.0.4.tgz#98f83d0bf8b5a2a4d988cb07f5cceda2e1bc1a09"
integrity sha512-AKhKCUXx8vdHIgyXUfbSLitfYor5dJbo4qMRRGqIJVSj+cyMkpgYbGjqr24GGijgZxdmOGL+s1b9iCUbIPgLNQ==
"@abp/font-awesome@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-3.0.5.tgz#62d58c34f0abf268e30bdf788dfbd3f45b19b412"
integrity sha512-/+31Vtw1S9vvuNy/rN/Q6awkgUaYNNYXESgfvqI0pqIkvlPgigvS8E68mHqGzsxyRqdZisI6JGd94AF+vJD7sQ==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
"@fortawesome/fontawesome-free" "^5.13.0"
"@abp/jquery-form@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-3.0.4.tgz#022eebbfe58b55e328984c0f119884c9464bc767"
integrity sha512-xDkk4rY0AmXBJE4qztzr2hZz+OVucVNWeagOEgeQHXJSKU/T+0kj0a1RxEHpyI24xBvEXAbe8PbByTbu0QXYew==
"@abp/jquery-form@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-3.0.5.tgz#9d2ea9b2509448ee163a163298a7a09480865f78"
integrity sha512-RKgvQciaMml33NvzA28aYyfNI+WQHRfKLFWvOvqc8qO4C3BvyrnwepuzZcnI/9UsELKLSLoFrfk3rJHhxnTJhg==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
jquery-form "^4.3.0"
"@abp/jquery-validation-unobtrusive@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.0.4.tgz#6cc942aadcdde33893e7a45c84aa022e033e4e8b"
integrity sha512-N9sza91HCyPvJo+d0RAfq4skae3TgAYg7KXhlwzRE2hqUQS+EICVARURWyxJW7s7Zu6+gxDTkXhBZRTW56deZA==
"@abp/jquery-validation-unobtrusive@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.0.5.tgz#b8912e1c4cfdd4fdff6ad4aa7ee8693b55a3be1f"
integrity sha512-kPifDA+1ErbeQBwn+BzwtWuTHXzBke/EMCmITuj59plM/60gZ1ZfVm8EzzTmKS7iLaVVeOqTKf0vEQTobNYffw==
dependencies:
"@abp/jquery-validation" "~3.0.4"
"@abp/jquery-validation" "~3.0.5"
jquery-validation-unobtrusive "^3.2.11"
"@abp/jquery-validation@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-3.0.4.tgz#3f96d1f83c75a7fc47e5e5e12c51dbc8da7582b1"
integrity sha512-2ljqas4YKU0PcwB0wQW2qhXejDHjW+N1XCxZDyCZ2wF9UxGlKBR8rFt76TJByXSotKE7n2hJRZaOh9ICZUow2g==
"@abp/jquery-validation@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-3.0.5.tgz#55e36b3c832a35447fac69af312dedf1dee22279"
integrity sha512-fdqxc5CJ6TpUAF8+NEsKCmXvvZiOPR5bzczcfvfovxLOjvHvdEO74XPGjsjlBNTdaKMhmNGZRK4JRLUz7fvTng==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
jquery-validation "^1.19.2"
"@abp/jquery@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-3.0.4.tgz#5f236d95cfae7f65012cd8778fd74490b7542442"
integrity sha512-g8N8Te5vf+8djdksfZoZ3tXaqlBC4Ra39pAWU/XerlLl64u+XmAX1s6yKRfC2kS8lf292GgiFYuNOkb77J6k4A==
"@abp/jquery@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-3.0.5.tgz#2e43ac19a8df6e1e0220d6d4991d33a93d890754"
integrity sha512-2d2l+smWKAVP4/b6GSO+8rmJ7pFXiUewpBDwkHm5qgkK6n6UGqIcK9lIgiS2YQ6rmL6qdtuNhhqtC9GF2TF2kQ==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
jquery "~3.5.1"
"@abp/lodash@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-3.0.4.tgz#ee60bc99f98b59fd9fc5ff4bbcf914a13faf209d"
integrity sha512-jxvv74Sm/fUKDBMylK4Ne/8GKm7GuHAdcLFtkoJQptdO+RjZpKJ86ULT+X5vbKskZaTj2fEmOR6l4hmfnBULTA==
"@abp/lodash@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-3.0.5.tgz#d5d9f14095d76239bc0e6f9804c24d1f92ffcab4"
integrity sha512-+B2vxfPwxoIIb8Ac8eHi6OgAn0qbjmPZuun7/es0/ouYYTbEhvhTAD20X3PGZlHrIYeDyS2ansuXm8gJI+/BzA==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
lodash "^4.17.15"
"@abp/luxon@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-3.0.4.tgz#aa1bf6a93d6254433101e50a205b276759969414"
integrity sha512-gX9dQJt9C6M1wmCSUIBoNr8CAt0jkUZFQ7LKKO/BoADGM/XFvY5vDbhL7zcwbwitgCOzmucoCxs/6rIxSOVlrg==
"@abp/luxon@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-3.0.5.tgz#083042c939ea58bf719e35114a6c8efb07b68688"
integrity sha512-0UVHO2CO89t34O35OA3JzWGWLBpg+e1NQXSfVtK90nmg/QmzGkHVM9m7Su0jjLNBt3N5EA5OPOhriot3bCKItw==
dependencies:
luxon "^1.24.1"
"@abp/malihu-custom-scrollbar-plugin@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-3.0.4.tgz#c54824b98363f4190cc6a37fcd421c2bfdc1e201"
integrity sha512-6LdSGctOr4ACtdohdSlb19vrFNJ5C0hy1VdbBEafHOBspXckvRVx4uJl2KfHmAC+bMg9apoK/wcHjVNdBHThYQ==
"@abp/malihu-custom-scrollbar-plugin@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-3.0.5.tgz#f908289927a63fd76fb8f60b00a37747d9edbe39"
integrity sha512-3GoXA8VtGyZWKiqaPCipPXYLLM7h2Y5Oecq5c5YlMQyW2akv7uHiQQF2GZW/nVsFu3HBbGTOWpKRRmVk3PEO9Q==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
malihu-custom-scrollbar-plugin "^3.1.5"
"@abp/select2@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-3.0.4.tgz#49db0cfbc9b08f6ae4949b2cb8bdef92d7d308dd"
integrity sha512-zKEgkWBFdGU2KiAMUKhucHqBfzDy2KNSefyqxpOiUd1L3PEKu+9o23HZRWzDupYltop+SO1Jw33CNoVAkKm4+A==
"@abp/select2@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-3.0.5.tgz#79d7754713c64a6549da520dc6867ef3e3dd0350"
integrity sha512-VbeSHdAdDy8exrjplHMMhLVV4oG7Gq9zBb5M52f0sIkA4DkXFhksXHcHNoOCfhaKAXpenGA03Z9GJewZ3MTpMw==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
select2 "^4.0.13"
"@abp/sweetalert@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-3.0.4.tgz#f85d11c5aabc6ceb509af1b8fd8d75bc3c74bd27"
integrity sha512-hO0qXUWxeMy/lfy3sb+5HvOmIPst6UvYJJGy3ZQR6DKEdkOktBX9sEDb7m6ij6xAIHqWIx1xLveul0oGteOG0Q==
"@abp/sweetalert@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-3.0.5.tgz#e3b92d23cf3633983980b6255bf63c43d422ceab"
integrity sha512-OUt4ANgj14GESfRG7+UqdONS8daOu+8x2OZDWv98hriuX0uCVBjwB2J0jRI/n653C68Mi0yHixGONDv9OLy/lg==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
sweetalert "^2.1.2"
"@abp/timeago@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-3.0.4.tgz#82d90a04f703a7db9c0acedcd2f3cea684e7ae18"
integrity sha512-7SbbqScTKCix25xWy5oLT0slJqUQsW6YgJgI0N41C3AsByRBXZeh5Gcep033gprTPmpgNY8JOWcEcCXySnYvfA==
"@abp/timeago@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-3.0.5.tgz#b191a7c43f3dc77645fe27273f2f6619f7d4eb31"
integrity sha512-QhvAOU4C+Qxh/gFuCSGZtLU4KwVhdv/jtK8ovWyqbGvQMCCIimqa8BFKIz0H5XWIJhHwwWblkpjICWgjWh6uXw==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
timeago "^1.6.7"
"@abp/toastr@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-3.0.4.tgz#884ab2343ddd59981c291c632e3b0630532f7e9c"
integrity sha512-NG0pVD8bdIXGpjPW9cozCzmyuAdnSLE4vDYLUReP1pZFp9B8SMJqpgPk9eFg0Y06aaHTVMKYBKRoq9xXS4w9Xg==
"@abp/toastr@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-3.0.5.tgz#fc83b678f5be3a22040d0421ad9db5b2eac9d4fc"
integrity sha512-qgi2DEu6FYpN/DYcOZrmXsAr1HqcIKMhBWeXgjfoYUIl/K5+OuUgbQ69D/jdc0jz8HU9a4Cw7Q8hhe4CkgssEw==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
toastr "^2.1.4"
"@abp/utils@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-3.0.4.tgz#38068fa8f735e1889cf2f226128366825948814d"
integrity sha512-TnXRZIZcxv5+EctEPsZEi2cyRXbpl3A679wzQa6BACv+pM45cjGO1JR8P0VswKN4TVL0aQRMv26N6U/uie8sEQ==
"@abp/utils@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-3.0.5.tgz#66b3da953500e73ff1f23d1b41168cde8216ce49"
integrity sha512-X9V1xLIEazItaf93nFSA4p94WT6QK3yA4eIyvU7GyyEkkOQdaakNwPbRwy930sd+I+/oXnIqM1vDsJu9QDhgvg==
dependencies:
just-compare "^1.3.0"

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json

@ -3,7 +3,7 @@
"name": "asp.net",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^3.0.4"
"@abp/aspnetcore.mvc.ui.theme.basic": "^3.0.5"
},
"devDependencies": {}
}

236
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock

@ -2,37 +2,37 @@
# yarn lockfile v1
"@abp/aspnetcore.mvc.ui.theme.basic@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-3.0.4.tgz#c58e3e06ba78279875cf2b5f96d34b83ce09f6df"
integrity sha512-tKKqVRKpwN7P4Mn+he3bcz4Qbt8bkZ++wk6iOujd6HLTkBNa0uKyM4L6+iCn1Hi62S6HeK2Xytx+b+IYeIlsuw==
dependencies:
"@abp/aspnetcore.mvc.ui.theme.shared" "~3.0.4"
"@abp/aspnetcore.mvc.ui.theme.shared@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-3.0.4.tgz#50db612b6492d36c63dd9042000a9fb00fecec54"
integrity sha512-imd2T5XXkNvGQyXV/A9hA80qSeurKaUcW6HDkte8Y2fs4QfvkjgzWXi7neqF6F+HVbYYPg3rIBWC6QF1OXGn2w==
dependencies:
"@abp/aspnetcore.mvc.ui" "~3.0.4"
"@abp/bootstrap" "~3.0.4"
"@abp/bootstrap-datepicker" "~3.0.4"
"@abp/datatables.net-bs4" "~3.0.4"
"@abp/font-awesome" "~3.0.4"
"@abp/jquery-form" "~3.0.4"
"@abp/jquery-validation-unobtrusive" "~3.0.4"
"@abp/lodash" "~3.0.4"
"@abp/luxon" "~3.0.4"
"@abp/malihu-custom-scrollbar-plugin" "~3.0.4"
"@abp/select2" "~3.0.4"
"@abp/sweetalert" "~3.0.4"
"@abp/timeago" "~3.0.4"
"@abp/toastr" "~3.0.4"
"@abp/aspnetcore.mvc.ui@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-3.0.4.tgz#4e4344d95a98a60c53037b351cb915d8ff7a7dcb"
integrity sha512-p7KI5CKkpD4EYzG4fCtygrtZhwMu7Z+IZFGbdqXelLbI5ACkbJyrn+N+9trPfS9papymeXY4p1E3v6xOH7rSHQ==
"@abp/aspnetcore.mvc.ui.theme.basic@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-3.0.5.tgz#c146f20226522f0305cc0fa31cfcf9d39be78901"
integrity sha512-ptNhD4xV2wSLW7vQ5KUgaRiH5Ov1OscZW2ZEoivnt3kqgtIC3DGw+SiE9vjFCvyY83fjRkIlW2PIfQxSySbteQ==
dependencies:
"@abp/aspnetcore.mvc.ui.theme.shared" "~3.0.5"
"@abp/aspnetcore.mvc.ui.theme.shared@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-3.0.5.tgz#5b370902cf229963f9aa10dd64482d57299dde76"
integrity sha512-iobAH39xrCdnqdaABaiYZg4lqNSOwPkMCLnyc+ytxOMbu/YnNKahXPmRhgd8dR/l+iQY1+6T4SWfLdGswi3gAg==
dependencies:
"@abp/aspnetcore.mvc.ui" "~3.0.5"
"@abp/bootstrap" "~3.0.5"
"@abp/bootstrap-datepicker" "~3.0.5"
"@abp/datatables.net-bs4" "~3.0.5"
"@abp/font-awesome" "~3.0.5"
"@abp/jquery-form" "~3.0.5"
"@abp/jquery-validation-unobtrusive" "~3.0.5"
"@abp/lodash" "~3.0.5"
"@abp/luxon" "~3.0.5"
"@abp/malihu-custom-scrollbar-plugin" "~3.0.5"
"@abp/select2" "~3.0.5"
"@abp/sweetalert" "~3.0.5"
"@abp/timeago" "~3.0.5"
"@abp/toastr" "~3.0.5"
"@abp/aspnetcore.mvc.ui@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-3.0.5.tgz#a050eaee328c56a3730f5abbf25035adf612d6f0"
integrity sha512-/EOgjXTzR+I1OK9KA7L3yzQ2RGSDW97+n3cQrSdLAUF+tnRvMJS0nytDlSJSRK2hb8/clFMaofZHCC81aUkYUg==
dependencies:
ansi-colors "^4.1.1"
extend-object "^1.0.0"
@ -41,144 +41,144 @@
path "^0.12.7"
rimraf "^3.0.2"
"@abp/bootstrap-datepicker@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-3.0.4.tgz#d21963f158a4d348a0acdc46209e67378aae8e55"
integrity sha512-25e9qm39dj9AijIchQIdLlBiBM5/fhASkEuLKNt6M8S/e3qwKLwbRhX90DKMFvOyKdxxcK1gX7tDuGHyG9A8mA==
"@abp/bootstrap-datepicker@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-3.0.5.tgz#3a7fd86dbcadc31c11be5b2b31c8cf8ac3035540"
integrity sha512-vIY1wT4RHoJAPhVK11Q5MS71iZbwsmM/0VTBvvs04mjRSSi6xhdyRYkA+l+P49fnOwhZNaaK19XwQdVVScDuhA==
dependencies:
bootstrap-datepicker "^1.9.0"
"@abp/bootstrap@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-3.0.4.tgz#3b0e2b74dc174a04ce669de1b140df833eee6031"
integrity sha512-DwxL1fGYOwChPwmMpwUxCKiIdzTfRf/YDkOLUiwI8eRrpABBeuE2sSHKfks6Rqh9Wqo/nlWXvfccgDRqweqhsA==
"@abp/bootstrap@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-3.0.5.tgz#91f2c8fe6a57fc5789ae8fe9c206b2c159e55880"
integrity sha512-LdydeJIbw5k1k7FkWaI1lTectZeTXmEIoccJtj9Qx4N9yGt40wGsjz9SOvUdJ0IKAgntIWNQP8ohWiBPgzT7Xg==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
bootstrap "^4.5.0"
bootstrap-v4-rtl "4.4.1-2"
"@abp/core@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-3.0.4.tgz#281940525e280e9e3a1aa4cb4e8f64548ea026a3"
integrity sha512-ZPEH1fa5bf/f1ek9ZLQHx7u78iF6kRZ8Z2NBI36cNL13RyQbx485qJF+KXyqNlJoJAh0CAs0NsAJADU3utcDNA==
"@abp/core@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-3.0.5.tgz#9a7bd990d02edc8128a5be3233b6fbb5669b15b8"
integrity sha512-LdkSkZQfN4hugcGNxBkje0+eQomivwNFbnAkMFQbvCwYLwKB2yvk7GCemEetW1piqvyrUGz4jqhtunjoXps5JA==
dependencies:
"@abp/utils" "^3.0.4"
"@abp/utils" "^3.0.5"
"@abp/datatables.net-bs4@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-3.0.4.tgz#e488336bd2c199146ea60464e0ba75aeb134957e"
integrity sha512-ID1XWKPxG9pv6mxBkku7JWvgXjTxJ1uShxmYv5kFYVuwOGMSvj8QVZInTHFcMdECopV56yjGPNOIFLeQj4Mw8Q==
"@abp/datatables.net-bs4@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-3.0.5.tgz#9b79935970d2d4074994240b5deb01dff5987c0a"
integrity sha512-jIWRBEG/NlhaE43i6Xzr18XJ5aTWA+WWslf7WgNOArG5slGBbH4lLLja4HswLP3NGdSmzIyHJR1p9XQ82nBkbQ==
dependencies:
"@abp/datatables.net" "~3.0.4"
"@abp/datatables.net" "~3.0.5"
datatables.net-bs4 "^1.10.21"
"@abp/datatables.net@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-3.0.4.tgz#7ef143b83143ad8d769be25296411a812a17b255"
integrity sha512-gdQwRuxO5fQWhG/DiUaf40DQ8dWIVGhOCyt6SZ8LQzq4uSaQqa0qrtTTTW2/aiee4MgJu2vJWYF5YCszIR/HhA==
"@abp/datatables.net@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-3.0.5.tgz#0a0a39e8fa54522bf8a09ce76ff3927207a79491"
integrity sha512-HClQ2tcZuWBAChf9oOJOtMP1zHv8jqloPEcw3iqbhS/tWZBw5VQ4TO+BVfZsqHDXZNnWSmR6/2xiKsK32nliew==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
datatables.net "^1.10.21"
"@abp/font-awesome@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-3.0.4.tgz#98f83d0bf8b5a2a4d988cb07f5cceda2e1bc1a09"
integrity sha512-AKhKCUXx8vdHIgyXUfbSLitfYor5dJbo4qMRRGqIJVSj+cyMkpgYbGjqr24GGijgZxdmOGL+s1b9iCUbIPgLNQ==
"@abp/font-awesome@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-3.0.5.tgz#62d58c34f0abf268e30bdf788dfbd3f45b19b412"
integrity sha512-/+31Vtw1S9vvuNy/rN/Q6awkgUaYNNYXESgfvqI0pqIkvlPgigvS8E68mHqGzsxyRqdZisI6JGd94AF+vJD7sQ==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
"@fortawesome/fontawesome-free" "^5.13.0"
"@abp/jquery-form@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-3.0.4.tgz#022eebbfe58b55e328984c0f119884c9464bc767"
integrity sha512-xDkk4rY0AmXBJE4qztzr2hZz+OVucVNWeagOEgeQHXJSKU/T+0kj0a1RxEHpyI24xBvEXAbe8PbByTbu0QXYew==
"@abp/jquery-form@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-3.0.5.tgz#9d2ea9b2509448ee163a163298a7a09480865f78"
integrity sha512-RKgvQciaMml33NvzA28aYyfNI+WQHRfKLFWvOvqc8qO4C3BvyrnwepuzZcnI/9UsELKLSLoFrfk3rJHhxnTJhg==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
jquery-form "^4.3.0"
"@abp/jquery-validation-unobtrusive@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.0.4.tgz#6cc942aadcdde33893e7a45c84aa022e033e4e8b"
integrity sha512-N9sza91HCyPvJo+d0RAfq4skae3TgAYg7KXhlwzRE2hqUQS+EICVARURWyxJW7s7Zu6+gxDTkXhBZRTW56deZA==
"@abp/jquery-validation-unobtrusive@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.0.5.tgz#b8912e1c4cfdd4fdff6ad4aa7ee8693b55a3be1f"
integrity sha512-kPifDA+1ErbeQBwn+BzwtWuTHXzBke/EMCmITuj59plM/60gZ1ZfVm8EzzTmKS7iLaVVeOqTKf0vEQTobNYffw==
dependencies:
"@abp/jquery-validation" "~3.0.4"
"@abp/jquery-validation" "~3.0.5"
jquery-validation-unobtrusive "^3.2.11"
"@abp/jquery-validation@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-3.0.4.tgz#3f96d1f83c75a7fc47e5e5e12c51dbc8da7582b1"
integrity sha512-2ljqas4YKU0PcwB0wQW2qhXejDHjW+N1XCxZDyCZ2wF9UxGlKBR8rFt76TJByXSotKE7n2hJRZaOh9ICZUow2g==
"@abp/jquery-validation@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-3.0.5.tgz#55e36b3c832a35447fac69af312dedf1dee22279"
integrity sha512-fdqxc5CJ6TpUAF8+NEsKCmXvvZiOPR5bzczcfvfovxLOjvHvdEO74XPGjsjlBNTdaKMhmNGZRK4JRLUz7fvTng==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
jquery-validation "^1.19.2"
"@abp/jquery@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-3.0.4.tgz#5f236d95cfae7f65012cd8778fd74490b7542442"
integrity sha512-g8N8Te5vf+8djdksfZoZ3tXaqlBC4Ra39pAWU/XerlLl64u+XmAX1s6yKRfC2kS8lf292GgiFYuNOkb77J6k4A==
"@abp/jquery@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-3.0.5.tgz#2e43ac19a8df6e1e0220d6d4991d33a93d890754"
integrity sha512-2d2l+smWKAVP4/b6GSO+8rmJ7pFXiUewpBDwkHm5qgkK6n6UGqIcK9lIgiS2YQ6rmL6qdtuNhhqtC9GF2TF2kQ==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
jquery "~3.5.1"
"@abp/lodash@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-3.0.4.tgz#ee60bc99f98b59fd9fc5ff4bbcf914a13faf209d"
integrity sha512-jxvv74Sm/fUKDBMylK4Ne/8GKm7GuHAdcLFtkoJQptdO+RjZpKJ86ULT+X5vbKskZaTj2fEmOR6l4hmfnBULTA==
"@abp/lodash@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-3.0.5.tgz#d5d9f14095d76239bc0e6f9804c24d1f92ffcab4"
integrity sha512-+B2vxfPwxoIIb8Ac8eHi6OgAn0qbjmPZuun7/es0/ouYYTbEhvhTAD20X3PGZlHrIYeDyS2ansuXm8gJI+/BzA==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
lodash "^4.17.15"
"@abp/luxon@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-3.0.4.tgz#aa1bf6a93d6254433101e50a205b276759969414"
integrity sha512-gX9dQJt9C6M1wmCSUIBoNr8CAt0jkUZFQ7LKKO/BoADGM/XFvY5vDbhL7zcwbwitgCOzmucoCxs/6rIxSOVlrg==
"@abp/luxon@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-3.0.5.tgz#083042c939ea58bf719e35114a6c8efb07b68688"
integrity sha512-0UVHO2CO89t34O35OA3JzWGWLBpg+e1NQXSfVtK90nmg/QmzGkHVM9m7Su0jjLNBt3N5EA5OPOhriot3bCKItw==
dependencies:
luxon "^1.24.1"
"@abp/malihu-custom-scrollbar-plugin@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-3.0.4.tgz#c54824b98363f4190cc6a37fcd421c2bfdc1e201"
integrity sha512-6LdSGctOr4ACtdohdSlb19vrFNJ5C0hy1VdbBEafHOBspXckvRVx4uJl2KfHmAC+bMg9apoK/wcHjVNdBHThYQ==
"@abp/malihu-custom-scrollbar-plugin@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-3.0.5.tgz#f908289927a63fd76fb8f60b00a37747d9edbe39"
integrity sha512-3GoXA8VtGyZWKiqaPCipPXYLLM7h2Y5Oecq5c5YlMQyW2akv7uHiQQF2GZW/nVsFu3HBbGTOWpKRRmVk3PEO9Q==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
malihu-custom-scrollbar-plugin "^3.1.5"
"@abp/select2@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-3.0.4.tgz#49db0cfbc9b08f6ae4949b2cb8bdef92d7d308dd"
integrity sha512-zKEgkWBFdGU2KiAMUKhucHqBfzDy2KNSefyqxpOiUd1L3PEKu+9o23HZRWzDupYltop+SO1Jw33CNoVAkKm4+A==
"@abp/select2@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-3.0.5.tgz#79d7754713c64a6549da520dc6867ef3e3dd0350"
integrity sha512-VbeSHdAdDy8exrjplHMMhLVV4oG7Gq9zBb5M52f0sIkA4DkXFhksXHcHNoOCfhaKAXpenGA03Z9GJewZ3MTpMw==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
select2 "^4.0.13"
"@abp/sweetalert@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-3.0.4.tgz#f85d11c5aabc6ceb509af1b8fd8d75bc3c74bd27"
integrity sha512-hO0qXUWxeMy/lfy3sb+5HvOmIPst6UvYJJGy3ZQR6DKEdkOktBX9sEDb7m6ij6xAIHqWIx1xLveul0oGteOG0Q==
"@abp/sweetalert@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-3.0.5.tgz#e3b92d23cf3633983980b6255bf63c43d422ceab"
integrity sha512-OUt4ANgj14GESfRG7+UqdONS8daOu+8x2OZDWv98hriuX0uCVBjwB2J0jRI/n653C68Mi0yHixGONDv9OLy/lg==
dependencies:
"@abp/core" "~3.0.4"
"@abp/core" "~3.0.5"
sweetalert "^2.1.2"
"@abp/timeago@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-3.0.4.tgz#82d90a04f703a7db9c0acedcd2f3cea684e7ae18"
integrity sha512-7SbbqScTKCix25xWy5oLT0slJqUQsW6YgJgI0N41C3AsByRBXZeh5Gcep033gprTPmpgNY8JOWcEcCXySnYvfA==
"@abp/timeago@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-3.0.5.tgz#b191a7c43f3dc77645fe27273f2f6619f7d4eb31"
integrity sha512-QhvAOU4C+Qxh/gFuCSGZtLU4KwVhdv/jtK8ovWyqbGvQMCCIimqa8BFKIz0H5XWIJhHwwWblkpjICWgjWh6uXw==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
timeago "^1.6.7"
"@abp/toastr@~3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-3.0.4.tgz#884ab2343ddd59981c291c632e3b0630532f7e9c"
integrity sha512-NG0pVD8bdIXGpjPW9cozCzmyuAdnSLE4vDYLUReP1pZFp9B8SMJqpgPk9eFg0Y06aaHTVMKYBKRoq9xXS4w9Xg==
"@abp/toastr@~3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-3.0.5.tgz#fc83b678f5be3a22040d0421ad9db5b2eac9d4fc"
integrity sha512-qgi2DEu6FYpN/DYcOZrmXsAr1HqcIKMhBWeXgjfoYUIl/K5+OuUgbQ69D/jdc0jz8HU9a4Cw7Q8hhe4CkgssEw==
dependencies:
"@abp/jquery" "~3.0.4"
"@abp/jquery" "~3.0.5"
toastr "^2.1.4"
"@abp/utils@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-3.0.4.tgz#38068fa8f735e1889cf2f226128366825948814d"
integrity sha512-TnXRZIZcxv5+EctEPsZEi2cyRXbpl3A679wzQa6BACv+pM45cjGO1JR8P0VswKN4TVL0aQRMv26N6U/uie8sEQ==
"@abp/utils@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-3.0.5.tgz#66b3da953500e73ff1f23d1b41168cde8216ce49"
integrity sha512-X9V1xLIEazItaf93nFSA4p94WT6QK3yA4eIyvU7GyyEkkOQdaakNwPbRwy930sd+I+/oXnIqM1vDsJu9QDhgvg==
dependencies:
just-compare "^1.3.0"

58
framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs

@ -3,7 +3,9 @@ using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.BlobStoring.TestObjects;
using Volo.Abp.Clients;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Testing;
using Xunit;
@ -14,11 +16,14 @@ namespace Volo.Abp.BlobStoring
{
protected IBlobContainer<TestContainer1> Container { get; }
protected ICurrentTenant CurrentTenant { get; }
protected BlobContainer_Tests()
{
Container = GetRequiredService<IBlobContainer<TestContainer1>>();
CurrentTenant = GetRequiredService<ICurrentTenant>();
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
@ -37,35 +42,68 @@ namespace Volo.Abp.BlobStoring
result.SequenceEqual(testContent).ShouldBeTrue();
}
[Fact]
public async Task Should_Save_And_Get_Blobs_In_Different_Tenant()
{
var blobName = "test-blob-1";
var testContent = "test content".GetBytes();
using (CurrentTenant.Change(Guid.NewGuid()))
{
await Container.SaveAsync(blobName, testContent);
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
}
using (CurrentTenant.Change(Guid.NewGuid()))
{
await Container.SaveAsync(blobName, testContent);
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
using (CurrentTenant.Change(null))
{
// Could not found the requested BLOB...
await Assert.ThrowsAsync<AbpException>(async () =>
await Container.GetAllBytesAsync(blobName)
);
}
}
using (CurrentTenant.Change(null))
{
await Container.SaveAsync(blobName, testContent);
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
}
}
[Fact]
public async Task Should_Overwrite_Pre_Saved_Blob_If_Requested()
{
var blobName = "test-blob-1";
var testContent = "test content".GetBytes();
await Container.SaveAsync(blobName, testContent);
var testContentOverwritten = "test content overwritten".GetBytes();
await Container.SaveAsync(blobName, testContentOverwritten, true);
var result = await Container.GetAllBytesAsync(blobName);
result.SequenceEqual(testContentOverwritten).ShouldBeTrue();
}
[Fact]
public async Task Should_Not_Allow_To_Overwrite_Pre_Saved_Blob_By_Default()
{
var blobName = "test-blob-1";
var testContent = "test content".GetBytes();
await Container.SaveAsync(blobName, testContent);
var testContentOverwritten = "test content overwritten".GetBytes();
await Assert.ThrowsAsync<BlobAlreadyExistsException>(() =>
Container.SaveAsync(blobName, testContentOverwritten)
);
}
[Theory]
[InlineData("test-blob-1")]
[InlineData("test-blob-1.txt")]
@ -91,7 +129,7 @@ namespace Volo.Abp.BlobStoring
await Container.DeleteAsync(blobName);
(await Container.ExistsAsync(blobName)).ShouldBeFalse();
}
[Theory]
[InlineData("test-blob-1")]
[InlineData("test-blob-1.txt")]
@ -102,4 +140,4 @@ namespace Volo.Abp.BlobStoring
(await Container.ExistsAsync(blobName)).ShouldBeFalse();
}
}
}
}

18
framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs

@ -7,6 +7,20 @@ namespace System.Collections.Generic
{
public class AbpListExtensions_Tests
{
[Fact]
public void InsertRange()
{
var list = Enumerable.Range(1, 3).ToList();
list.InsertRange(1, new[] {7, 8, 9});
list[0].ShouldBe(1);
list[1].ShouldBe(7);
list[2].ShouldBe(8);
list[3].ShouldBe(9);
list[4].ShouldBe(2);
list[5].ShouldBe(3);
}
[Fact]
public void InsertAfter()
{
@ -191,7 +205,7 @@ namespace System.Collections.Generic
{
var list = RandomHelper
.GenerateRandomizedList(new char[] {'A', 'B', 'C', 'D', 'E', 'F', 'G'});
list = list.SortByDependencies(c => dependencies[c]);
foreach (var dependency in dependencies)
@ -204,4 +218,4 @@ namespace System.Collections.Generic
}
}
}
}
}

7
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"HelloText": "مرحبا {0}",
"HowAreYou": "كيف حالكم ؟"
}
}

3
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json

@ -43,6 +43,7 @@
"Description:Abp.Account.EnableLocalLogin": "Indicates if the server will allow users to authenticate with a local account.",
"LoggedOutTitle": "Signed Out",
"LoggedOutText": "You have been signed out and you will be redirected soon.",
"ReturnToText": "Click here to redirect to {0}"
"ReturnToText": "Click here to redirect to {0}",
"OrLoginWith": "Or login with;"
}
}

81
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml

@ -6,34 +6,34 @@
@model Volo.Abp.Account.Web.Pages.Account.LoginModel
@inject IHtmlLocalizer<AccountResource> L
@inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin)
{
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["Login"]</h4>
@if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled))
{
<strong>
@L["AreYouANewUser"]
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Register"]</a>
</strong>
}
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["Login"]</h4>
@if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled))
{
<strong>
@L["AreYouANewUser"]
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Register"]</a>
</strong>
}
@if (Model.EnableLocalLogin)
{
<form method="post" class="mt-4">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<input asp-for="ReturnUrl"/>
<input asp-for="ReturnUrlHash"/>
<div class="form-group">
<label asp-for="LoginInput.UserNameOrEmailAddress"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control"/>
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LoginInput.Password"></label>
<input asp-for="LoginInput.Password" class="form-control" />
<input asp-for="LoginInput.Password" class="form-control"/>
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input" />
<input asp-for="LoginInput.RememberMe" class="form-check-input"/>
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
@ -43,29 +43,30 @@
<abp-button type="submit" button-type="Secondary" formnovalidate="formnovalidate" name="Action" value="Cancel" class="btn-block btn-lg mt-3">@L["Cancel"]</abp-button>
}
</form>
</div>
</div>
}
}
@if (Model.VisibleExternalProviders.Any())
{
<div class="col-md-6">
<h4>@L["UseAnotherServiceToLogIn"]</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@if (Model.VisibleExternalProviders.Any())
{
<div class="mt-2">
<h5>@L["OrLoginWith"]</h5>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<input asp-for="ReturnUrl"/>
<input asp-for="ReturnUrlHash"/>
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary m-1" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
}
</div>

21
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -214,13 +214,23 @@ namespace Volo.Abp.Account.Web.Pages.Account
//TODO: Handle other cases for result!
// Get the information about the user from the external login provider
var info = await SignInManager.GetExternalLoginInfoAsync();
if (info == null)
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
throw new ApplicationException("Error loading external login information during confirmation.");
}
var user = await CreateExternalUserAsync(info);
if (!IsEmailRetrievedFromExternalLogin(externalLoginInfo))
{
return RedirectToPage("./Register", new
{
IsExternalLogin = true,
ExternalLoginAuthSchema = externalLoginInfo.LoginProvider,
ReturnUrl = returnUrl
});
}
var user = await CreateExternalUserAsync(externalLoginInfo);
await SignInManager.SignInAsync(user, false);
@ -234,6 +244,11 @@ namespace Volo.Abp.Account.Web.Pages.Account
return RedirectSafely(returnUrl, returnUrlHash);
}
private static bool IsEmailRetrievedFromExternalLogin(ExternalLoginInfo externalLoginInfo)
{
return externalLoginInfo.Principal.FindFirstValue(AbpClaimTypes.Email) != null;
}
protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
{
var emailAddress = info.Principal.FindFirstValue(AbpClaimTypes.Email);

37
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml

@ -4,18 +4,27 @@
@model Volo.Abp.Account.Web.Pages.Account.RegisterModel
@inject IHtmlLocalizer<AccountResource> L
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["Register"]</h4>
<strong>
@L["AlreadyRegistered"]
<a href="@Url.Page("./Login", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Login"]</a>
</strong>
<form method="post" class="mt-4">
<abp-input asp-for="Input.UserName" auto-focus="true" />
<abp-input asp-for="Input.EmailAddress" />
<abp-input asp-for="Input.Password" />
<abp-button button-type="Primary" type="submit" class="btn-lg btn-block mt-4">@L["Register"]</abp-button>
</form>
</div>
<div class="card mt-3 shadow-sm rounded">
<div class="card-body p-5">
<h4>@L["Register"]</h4>
<strong>
@L["AlreadyRegistered"]
<a href="@Url.Page("./Login", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Login"]</a>
</strong>
<form method="post" class="mt-4">
@if (!Model.IsExternalLogin)
{
<abp-input asp-for="Input.UserName" auto-focus="true"/>
}
<abp-input asp-for="Input.EmailAddress"/>
@if (!Model.IsExternalLogin)
{
<abp-input asp-for="Input.Password"/>
}
<abp-button button-type="Primary" type="submit" class="btn-lg btn-block mt-4">@L["Register"]</abp-button>
</form>
</div>
</div>

141
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

@ -1,14 +1,16 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Volo.Abp.Account.Settings;
using Volo.Abp.Auditing;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
@ -27,6 +29,12 @@ namespace Volo.Abp.Account.Web.Pages.Account
[BindProperty]
public PostInput Input { get; set; }
[BindProperty(SupportsGet = true)]
public bool IsExternalLogin { get; set; }
[BindProperty(SupportsGet = true)]
public string ExternalLoginAuthSchema { get; set; }
public RegisterModel(IAccountAppService accountAppService)
{
AccountAppService = accountAppService;
@ -35,34 +43,141 @@ namespace Volo.Abp.Account.Web.Pages.Account
public virtual async Task<IActionResult> OnGetAsync()
{
await CheckSelfRegistrationAsync();
await TrySetEmailAsync();
return Page();
}
public virtual async Task<IActionResult> OnPostAsync()
private async Task TrySetEmailAsync()
{
ValidateModel();
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
return;
}
if (!externalLoginInfo.Principal.Identities.Any())
{
return;
}
var identity = externalLoginInfo.Principal.Identities.First();
var emailClaim = identity.FindFirst(ClaimTypes.Email);
if (emailClaim == null)
{
return;
}
Input = new PostInput {EmailAddress = emailClaim.Value};
}
}
public virtual async Task<IActionResult> OnPostAsync()
{
await CheckSelfRegistrationAsync();
var registerDto = new RegisterDto
var registerDto = new RegisterDto()
{
AppName = "MVC",
EmailAddress = Input.EmailAddress,
Password = Input.Password,
UserName = Input.UserName
AppName = "MVC"
};
var userDto = await AccountAppService.RegisterAsync(registerDto);
var user = await UserManager.GetByIdAsync(userDto.Id);
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
registerDto.EmailAddress = Input.EmailAddress;
registerDto.UserName = Input.EmailAddress;
registerDto.Password = GeneratePassword();
}
else
{
ValidateModel();
await UserManager.SetEmailAsync(user, Input.EmailAddress);
registerDto.EmailAddress = Input.EmailAddress;
registerDto.Password = Input.Password;
registerDto.UserName = Input.UserName;
}
var userDto = await AccountAppService.RegisterAsync(registerDto);
var user = await UserManager.GetByIdAsync(userDto.Id);
await SignInManager.SignInAsync(user, isPersistent: false);
if (IsExternalLogin)
{
await AddToUserLogins(user);
}
return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
protected virtual async Task AddToUserLogins(IdentityUser user)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
var userLoginAlreadyExists = user.Logins.Any(x =>
x.TenantId == user.TenantId &&
x.LoginProvider == externalLoginInfo.LoginProvider &&
x.ProviderKey == externalLoginInfo.ProviderKey);
if (!userLoginAlreadyExists)
{
user.AddLogin(new UserLoginInfo(
externalLoginInfo.LoginProvider,
externalLoginInfo.ProviderKey,
externalLoginInfo.ProviderDisplayName
)
);
}
}
protected virtual string GeneratePassword()
{
var random = new Random();
var options = UserManager.Options.Password;
int length = random.Next(options.RequiredLength, IdentityUserConsts.MaxPasswordLength - 1);
bool nonAlphanumeric = options.RequireNonAlphanumeric;
bool digit = options.RequireDigit;
bool lowercase = options.RequireLowercase;
bool uppercase = options.RequireUppercase;
StringBuilder password = new StringBuilder();
while (password.Length < length)
{
char c = (char)random.Next(32, 126);
password.Append(c);
if (char.IsDigit(c))
digit = false;
else if (char.IsLower(c))
lowercase = false;
else if (char.IsUpper(c))
uppercase = false;
else if (!char.IsLetterOrDigit(c))
nonAlphanumeric = false;
}
if (nonAlphanumeric)
password.Append((char)random.Next(33, 48));
if (digit)
password.Append((char)random.Next(48, 58));
if (lowercase)
password.Append((char)random.Next(97, 123));
if (uppercase)
password.Append((char)random.Next(65, 91));
return password.ToString();
}
protected virtual async Task CheckSelfRegistrationAsync()
{
if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled) ||

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

Loading…
Cancel
Save