@ -0,0 +1,64 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Permission:CommunityArticle": "社区文章", |
|||
"Permission:Edit": "修改", |
|||
"Waiting": "等待中", |
|||
"Approved": "已批准", |
|||
"Rejected": "已拒绝", |
|||
"Wait": "等待", |
|||
"Approve": "批准", |
|||
"Reject": "拒绝", |
|||
"ReadArticle": "阅读文章", |
|||
"Status": "状态", |
|||
"ContentSource": "内容来源", |
|||
"Details": "详情", |
|||
"Url": "url", |
|||
"Title": "标题", |
|||
"CreationTime": "创建时间", |
|||
"Save": "保存", |
|||
"SameUrlAlreadyExist": "url已存在,如果你想要添加这篇文章,你需要更改url!", |
|||
"UrlIsNotValid": "Url无效.", |
|||
"UrlNotFound" : "Url未找到.", |
|||
"UrlContentNotFound": "Url内容未找到.", |
|||
"Summary": "摘要", |
|||
"MostRead": "阅读最多", |
|||
"Latest": "最新", |
|||
"ContributeAbpCommunity": "为ABP社区做贡献", |
|||
"SubmitYourArticle": "提交你的文章", |
|||
"ContributionGuide": "贡献指南", |
|||
"BugReport": "Bug报告", |
|||
"SeeAllArticles": "查看所有的文章", |
|||
"WelcomeToABPCommunity!": "欢迎来到ABP社区!", |
|||
"MyProfile": "我的资料", |
|||
"MyOrganizations": "我的组织", |
|||
"EmailNotValid": "请输入有效的电子邮箱地址.", |
|||
"FeatureRequest": "功能请求", |
|||
"CreateArticleTitleInfo": "文章标题显示在文章列表中.", |
|||
"CreateArticleUrlInfo": "文章的原始GitHub/外部URL.", |
|||
"CreateArticleSummaryInfo": "文章的简短摘要将显示在文章列表中.", |
|||
"CreateArticleCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!", |
|||
"ThisExtensionIsNotAllowed": "不允许此扩展名.", |
|||
"TheFileIsTooLarge": "文件过大.", |
|||
"GoToTheArticle": "转到文章", |
|||
"Contribute": "贡献", |
|||
"OverallProgress": "总体流程", |
|||
"Done": "完成", |
|||
"Open": "打开", |
|||
"Closed": "关闭", |
|||
"LatestQuestionOnThe": "有关的最新问题", |
|||
"Stackoverflow": "Stackoverflow", |
|||
"Votes": "票数", |
|||
"Answer": "回答", |
|||
"Views": "观看次数", |
|||
"Answered": "已回答", |
|||
"WaitingForYourAnswer": "等待你的回答", |
|||
"Asked": "提问", |
|||
"AllQuestions": "所有的问题", |
|||
"NextVersion": "下一个版本", |
|||
"MilestoneErrorMessage": "无法从Github获取当前的里程碑详细信息.", |
|||
"QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.", |
|||
"Oops": "哎呀!" |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,263 @@ |
|||
# ABP Framework v3.1 RC Has Been Released |
|||
|
|||
Today, we are releasing the **ABP Framework version 3.1 Release Candidate** (RC). The development cycle for this version was **~7 weeks**. It was the longest development cycle for a feature version release ever. We have completed **~150 issues**, merged **~150 PRs** and made **~1,000 commits** only in the main [abp repository](https://github.com/abpframework/abp). See the related [milestone](https://github.com/abpframework/abp/milestone/38?closed=1) on GitHub. |
|||
|
|||
There were two main reasons of this long development cycle; |
|||
|
|||
* We've switched to **4-weeks** release cycle (was discussed in [this issue](https://github.com/abpframework/abp/issues/4692)). |
|||
* We've [re-written](https://github.com/abpframework/abp/issues/4881) the Angular service proxy generation system using the Angular schematics to make it more stable. There were some problems with the previous implementation. |
|||
|
|||
This long development cycle brings a lot of new features, improvements and bug fixes. I will highlight the fundamental features and changes in this blog post. |
|||
|
|||
## About the Preview/Stable Version Cycle |
|||
|
|||
As mentioned above, it is planned to release a new stable feature version (like 3.1, 3.2, 3.3...) in every 4-weeks. |
|||
|
|||
In addition, we are starting to deploy a **preview version** 2-weeks before the stable versions for every feature/major releases. |
|||
|
|||
Today, we've released `3.1.0-rc.1` as the first preview version. We may release more previews if it is needed until the stable 3.1.0 version. |
|||
|
|||
**The stable `3.1.0` version will be released on September 3, 2020.** Next RC version, `3.2.0-rc.1`, is planned for September 17, 2020 (2 weeks after the stable 3.1.0 and 2 weeks before the stable 3.2.0). |
|||
|
|||
We **won't add new features** to a version after publishing the preview version. We only will make **bug fixes** until the stable version. The new features being developed in this period will be available in the next version. |
|||
|
|||
> We will use `-rc.x` suffix (like `3.1.0-rc.1` and `3.1.0-rc.2`) for preview releases. However, we may also publish with `-preview.x` suffix before RC (Release Candidate) releases, especially for major versions (like 4.0, 5.0...). |
|||
|
|||
### About the Nightly Builds |
|||
|
|||
Don't confuse preview versions vs nightly builds. When we say preview, we are mentioning the preview system explained above. |
|||
|
|||
We will continue to publish **nightly builds** for all the [ABP Framework packages](https://abp.io/packages). Nightly pages are built from the development branch. You can refer to [this document](https://docs.abp.io/en/abp/latest/Nightly-Builds) to learn how to use the nightly packages. |
|||
|
|||
## Get Started with the RC Versions |
|||
|
|||
Please try the preview versions and provide feedback to us to release more stable versions. Please open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new) if you find a bug or want to give feedback. |
|||
|
|||
### Update the ABP CLI to the 3.1.0-rc.1 |
|||
|
|||
Since this is the first preview version, you need to upgrade the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) to the `3.1.0-rc.1` to be able to use the preview features: |
|||
|
|||
````bash |
|||
dotnet tool update Volo.Abp.Cli -g --version 3.1.0-rc.1 |
|||
```` |
|||
|
|||
### New Solutions |
|||
|
|||
The [ABP.IO](https://abp.io/) platform and the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) are compatible with the RC system. You can select the "preview" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the ABP CLI [new](https://docs.abp.io/en/abp/latest/CLI?_ga=2.106435654.411298747.1597771169-1910388957.1594128976#new) command: |
|||
|
|||
````bash |
|||
abp new Acme.BookStore --preview |
|||
```` |
|||
|
|||
This command will create a new project with the latest RC/Preview version. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution. |
|||
|
|||
### Existing Solutions |
|||
|
|||
If you already have a solution and want to use/test the latest RC/Preview version, use the `abp switch-to-preview` command in the root folder of your solution. You can return back to the latest stable using the `abp switch-to-stable ` command later. |
|||
|
|||
> Note that the `abp switch-to-preview` command was being used to switch to nightly builds before the v3.1. Now, you should use the `abp switch-to-nightly` for [nightly builds](https://docs.abp.io/en/abp/latest/Nightly-Builds). |
|||
|
|||
## Breaking Changes / Special Notes |
|||
|
|||
### ABP & ABP Commercial |
|||
|
|||
* If you are using **EF Core**, you may need to **add a new migration** after upgrading the packages. Just run the standard "Add-Migration" command, check the generated migration code and execute the "Update-Database" command to apply changes to the database. |
|||
* If you have implemented **social/external logins** for your MVC / Razor Page UI application before, you may want to check [this issue](https://github.com/abpframework/abp/issues/4981). We made some improvements and changes that you may want to take action for your application. Beginning from v3.1, the users created their accounts via social login can still set a local password to login with local username/email & password. |
|||
|
|||
### ABP Commercial Only |
|||
|
|||
* We've **moved favicons** into `/wwwroot/images/favicon/` folder for the ASP.NET Core **MVC / Razor Page UI** applications. There are 10 favicon related files (including the `favicon.ico`) under this directory to better work with different browser and cases. You can create a new application to check this folder and copy the files into your own application. Then you can customize the icons for your own brand (hint: you can use a tool [like that](https://realfavicongenerator.net/) to create the favicons with various formats). |
|||
* Removed direct **Twitter & Facebook social login integrations** from the [account module](https://commercial.abp.io/modules/Volo.Account.Pro), for **MVC / Razor Pages UI**. Follow [this documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Authentication/Social-External-Logins.md) to easily add social logins to your applications if you need. The account module provides all the infrastructure to handle social/external logins, you just need to configure it. |
|||
|
|||
## What's New with the ABP Framework 3.1 RC.1 |
|||
|
|||
### Angular Service Proxies |
|||
|
|||
ABP provides a system to generate Angular service proxies (with TypeScript) to consume the HTTP APIs of your application. Service proxy generation system **has been completely re-written** with the ABP Framework 3.1. The main goal was to build more stable and feature rich system that is better aligned with other ABP Framework features (like [modularity](https://docs.abp.io/en/abp/latest/Module-Development-Basics)). |
|||
|
|||
[See the documentation](https://docs.abp.io/en/abp/latest/UI/Angular/Service-Proxies) to learn more about the service proxy generation for Angular applications. |
|||
|
|||
### Authorization Code Flow for the Angular UI |
|||
|
|||
We were using the **resource owner password authentication** flow for the Angular UI login page. We've implemented **Authorization Code Flow** for the Angular account module and made it **default for new projects**. With this change, the Angular application now redirects to the login page of the MVC UI which was implemented using the Identity Server 4. We also removed the client secret from the Angular side with this change. |
|||
|
|||
Old behavior remains exist. If you want to switch to the new flow (which is recommended), follow the steps below: |
|||
|
|||
1) Add `authorization_code` to the `IdentityServerClientGrantTypes` table in the database, for the client used by the Angular UI (the `ClientId` is `YourProjectName_App` by default, in the `IdentityServerClients` table). |
|||
|
|||
2) Add `http://localhost:4200` to `IdentityServerClientRedirectUris` and `IdentityServerClientPostLogoutRedirectUris` tables for the same client. |
|||
|
|||
3) Set `RequireClientSecret` to `false` in the `IdentityServerClients` table for the same client. |
|||
|
|||
> [ABP Commercial](https://commercial.abp.io/) users can make these changes on the [Identity Server Management UI](https://commercial.abp.io/modules/Volo.Identityserver.Ui). |
|||
|
|||
4) Change the `oAuthConfig` section in the `src/environments/environment.ts` file of the Angular application. |
|||
|
|||
You can take [this new configuration](https://gist.github.com/hikalkan/e7f6ae7f507b201783682dccaeadc5e3) as a reference. Main changes are; |
|||
|
|||
* Added `responseType` as `code`. |
|||
* Added `redirectUri` |
|||
* Added `offline_access` to the `scope`. |
|||
* Removed `oidc: false` option. |
|||
* Removed the client secret option. |
|||
|
|||
### Global Feature System |
|||
|
|||
The new "Global Features" system allows to **enable/disable features of an application or a module** in a central point. It is especially useful if you want to use a module but don't want to bring all its features into your application. If the module was so designed, you can enable only the features you need. |
|||
|
|||
When you disable a feature; |
|||
|
|||
* The **database tables** related to that feature should not be created in the database. |
|||
* The **HTTP APIs** related to that feature should not be exposed. They returns 404 if they are directly requested. |
|||
|
|||
So, the goal is that; when you disable a feature, it should behave like that feature doesn't exists in your system at all. |
|||
|
|||
There is **no way to enable/disable a global feature on runtime**. You should decide it in the development time (remember, even database tables are not being created for disabled global features, so you can't enable it on runtime). |
|||
|
|||
> "Global Features" system is different than [SaaS/multi-tenancy features](https://docs.abp.io/en/abp/latest/Features), where you can enable/disable features for your tenants on runtime. |
|||
|
|||
Assume that you are using the [CMS Kit module](https://github.com/abpframework/abp/tree/dev/modules/cms-kit) (this module is in a very early stage) where you only want to enable the comment feature: |
|||
|
|||
````csharp |
|||
GlobalFeatureManager.Instance.Modules.CmsKit().Comments.Enable(); |
|||
```` |
|||
|
|||
You can check if a feature was enabled: |
|||
|
|||
```csharp |
|||
GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>(); |
|||
``` |
|||
|
|||
Or you can add `[RequiresGlobalFeature(...)]` attribute to a controller/page to disable it if the related feature was disabled: |
|||
|
|||
```csharp |
|||
//... |
|||
[RequiresGlobalFeature(typeof(CommentsFeature))] |
|||
public class CommentController : AbpController |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
See the issue [#5061](https://github.com/abpframework/abp/issues/5061) until this is fully documented. |
|||
|
|||
### Social/External Logins |
|||
|
|||
Implemented the infrastructure for social/external logins in the account module. So, now you can easily configure your application to support social/external logins by [following the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Authentication/Social-External-Logins.md). Once you configure a provider, a button will appear on the login page to use this provider. |
|||
|
|||
The social logins will work as expected even if you are using the Angular UI, since the Angular UI uses the MVC login using the authorization code flow implemented with this new version (as explained above). |
|||
|
|||
### Forgot/Reset Password |
|||
|
|||
Implemented forgot password / password reset for the account module. |
|||
|
|||
You can now enter your email address to get an email containing a **password reset link**: |
|||
|
|||
 |
|||
|
|||
When you click to the link, you are redirected to a password reset page to determine your new password: |
|||
|
|||
 |
|||
|
|||
### External Login System |
|||
|
|||
The standard Social/External Login system (like Facebook login) works via OpenID Connect. That means the user is redirected to the login provider, logins there and redirected to your application. |
|||
|
|||
While this is pretty nice for most scenarios, sometimes you want a simpler external login mechanism: User enters username & password in your own application's login form and you check the username & password from another source, not from your own database. |
|||
|
|||
ABP v3.1 introduces an External Login System to check username & password from any source (from an external database, a REST service or from an LDAP / Active Directory server). |
|||
|
|||
You can check the [issue #4977](https://github.com/abpframework/abp/issues/4977#issuecomment-670006297) until it is fully documented. |
|||
|
|||
We've implemented LDAP authentication for the ABP Commercial, using this new login extension system (see the ABP Commercial section below). |
|||
|
|||
### User Security Logs |
|||
|
|||
The new [Security Log System](https://github.com/abpframework/abp/issues/4492) (of the Identity module) automatically logs all authentication related operations (login, logout, change password...) to a `AbpSecurityLogs` table in the database. |
|||
|
|||
### New BLOB Storage Providers |
|||
|
|||
Implemented [AWS](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aws.md) and [Aliyun](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aliyun.md) providers for the [BLOB storing](https://docs.abp.io/en/abp/latest/Blob-Storing) system with this version. |
|||
|
|||
### Module Entity Extensibility |
|||
|
|||
We had introduced a entity extension system that allows to add new properties to existing entities of depended modules by a simple configuration. When you add a new property, it appears on the create, edit and list views on the UI and created a new field in the related database table. We've implemented this system for the identity and tenant management modules, so you can extend entities of these modules. See [the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Module-Entity-Extensions.md). |
|||
|
|||
### Other Features / Highlights |
|||
|
|||
Here, some other highlights from this release; |
|||
|
|||
* UOW level caching system [#4796](https://github.com/abpframework/abp/issues/4796) |
|||
* Refactored the console application template to better integrate to the host builder [#5006](https://github.com/abpframework/abp/issues/5006) |
|||
* [Volo.Abp.Ldap](https://www.nuget.org/packages/Volo.Abp.Ldap) package now supports multi-tenancy. |
|||
* Introduce `BasicAggregateRoot` base class [#4808](https://github.com/abpframework/abp/issues/4808) |
|||
* Sets GUID Id in the `InsertAsync` method of the EF Core repository if it was not set by the developer [#4634](https://github.com/abpframework/abp/pull/4634) |
|||
* Added `GetPagedListAsync` methods to the repository to simplify paging [#4617](https://github.com/abpframework/abp/pull/4617) |
|||
* Configured [Prettier](https://prettier.io/) for the startup template [#4318](https://github.com/abpframework/abp/issues/4318) |
|||
* Defined new layout hooks for the MVC UI: before page content & after page content [#4008](https://github.com/abpframework/abp/issues/4008) |
|||
* Allow to put static resources (js, css... files) under the Components folder for ASP.NET Core MVC UI. |
|||
* Upgraded to AutoMapper 10 and Quartz 3.1 for the related integration packages. |
|||
|
|||
## What's New with the ABP Commercial v3.1 RC.1 |
|||
|
|||
### Security Logs UI |
|||
|
|||
We've created a UI to report user security logs for authentication related operations, under the Identity Management menu: |
|||
|
|||
 |
|||
|
|||
Also, every user can see his/her own security logs by selecting the "My security logs" under the user menu: |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
### LDAP Authentication |
|||
|
|||
We've implemented LDAP authentication using the new external login system explained above. Also, created a UI to configure the server settings: |
|||
|
|||
 |
|||
|
|||
In this way, you can simply check passwords of the users from LDAP in the login page. If given username / password doesn't exists on LDAP, then it fallbacks to the local database, just like before. |
|||
|
|||
Since it supports **multi-tenancy**, you can enable, disable and configure it for your tenants. |
|||
|
|||
### Email / Phone Number Verification |
|||
|
|||
User profile management page now supports to Email & Phone Number verification flow: |
|||
|
|||
 |
|||
|
|||
When user clicks to the **verify** button, a verification email/SMS (that has a verification code) sent to the user and the UI waits to submit this code. |
|||
|
|||
### User Lock |
|||
|
|||
Implemented to **lock a user** for a given period of time. Locked users can not login to the application for the given period of time: |
|||
|
|||
 |
|||
|
|||
### ABP Suite: Angular UI Code Generation Revisited |
|||
|
|||
Angular UI code generation has been re-written using the Angular Schematics for the ABP Suite. It is now more stable and produces a better application code. |
|||
|
|||
ABP Suite also supports code generation on module development. |
|||
|
|||
### Others |
|||
|
|||
* **Social logins** and **authorization code flow** are also implemented for the ABP Commercial, just as described above. |
|||
* Added breadcrumb and file icons for the **file management module**. |
|||
|
|||
## The ABP Community |
|||
|
|||
We've lunched the [community.abp.io](https://community.abp.io/) ~two weeks ago with its initial version. It only has "Article submission" system for now. We are developing new exciting features. There will be an update in a few days and we'll publish a new blog post for it. |
|||
|
|||
## Conclusion |
|||
|
|||
The main goals of the 3.1 version were; |
|||
|
|||
* Complete the missing **authentication features** (like social logins, LDAP authentication, authorization code flow for the Angular UI...) for the ABP Framework & ABP Commercial. |
|||
* Re-write a stable and feature complete **Angular service proxy generation** system for the ABP Framework and CRUD UI generation system for the ABP Commercial. |
|||
* Develop a system to lunch **preview versions** of the platform. `3.1.0-rc.1` is the first preview version that has been published with this new system. |
|||
* Complete the fundamental **documentation & tutorials** (we've even created a [video tutorial series](https://www.youtube.com/watch?v=cJzyIFfAlp8&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ)). |
|||
|
|||
ABP.IO platform will be more mature & stable with the v3.1. Enjoy Coding! |
|||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,212 @@ |
|||
# How to fix the Chrome login issue for the IdentityServer4 |
|||
|
|||
## Introduction |
|||
|
|||
When you use HTTP on your Identity Server 4 enabled website, users may not login because of the changes made by Chrome in the version 8x. This occurs when you use HTTP schema in your website. The issue is explained here https://docs.microsoft.com/en-gb/dotnet/core/compatibility/3.0-3.1#http-browser-samesite-changes-impact-authentication |
|||
|
|||
## How to solve it? |
|||
|
|||
### Step-1 |
|||
|
|||
Create the below extension in your ***.Web** project. |
|||
|
|||
```csharp |
|||
using System; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection |
|||
{ |
|||
public static class SameSiteCookiesServiceCollectionExtensions |
|||
{ |
|||
/// <summary> |
|||
/// -1 defines the unspecified value, which tells ASPNET Core to NOT |
|||
/// send the SameSite attribute. With ASPNET Core 3.1 the |
|||
/// <seealso cref="SameSiteMode" /> enum will have a definition for |
|||
/// Unspecified. |
|||
/// </summary> |
|||
private const SameSiteMode Unspecified = (SameSiteMode)(-1); |
|||
|
|||
/// <summary> |
|||
/// Configures a cookie policy to properly set the SameSite attribute |
|||
/// for Browsers that handle unknown values as Strict. Ensure that you |
|||
/// add the <seealso cref="Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware" /> |
|||
/// into the pipeline before sending any cookies! |
|||
/// </summary> |
|||
/// <remarks> |
|||
/// Minimum ASPNET Core Version required for this code: |
|||
/// - 2.1.14 |
|||
/// - 2.2.8 |
|||
/// - 3.0.1 |
|||
/// - 3.1.0-preview1 |
|||
/// Starting with version 80 of Chrome (to be released in February 2020) |
|||
/// cookies with NO SameSite attribute are treated as SameSite=Lax. |
|||
/// In order to always get the cookies send they need to be set to |
|||
/// SameSite=None. But since the current standard only defines Lax and |
|||
/// Strict as valid values there are some browsers that treat invalid |
|||
/// values as SameSite=Strict. We therefore need to check the browser |
|||
/// and either send SameSite=None or prevent the sending of SameSite=None. |
|||
/// Relevant links: |
|||
/// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 |
|||
/// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00 |
|||
/// - https://www.chromium.org/updates/same-site |
|||
/// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ |
|||
/// - https://bugs.webkit.org/show_bug.cgi?id=198181 |
|||
/// </remarks> |
|||
/// <param name="services">The service collection to register <see cref="CookiePolicyOptions" /> into.</param> |
|||
/// <returns>The modified <see cref="IServiceCollection" />.</returns> |
|||
public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services) |
|||
{ |
|||
services.Configure<CookiePolicyOptions>(options => |
|||
{ |
|||
options.MinimumSameSitePolicy = Unspecified; |
|||
options.OnAppendCookie = cookieContext => |
|||
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); |
|||
options.OnDeleteCookie = cookieContext => |
|||
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); |
|||
}); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
private static void CheckSameSite(HttpContext httpContext, CookieOptions options) |
|||
{ |
|||
if (options.SameSite == SameSiteMode.None) |
|||
{ |
|||
var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); |
|||
|
|||
if (DisallowsSameSiteNone(userAgent)) |
|||
{ |
|||
options.SameSite = Unspecified; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Checks if the UserAgent is known to interpret an unknown value as Strict. |
|||
/// For those the <see cref="CookieOptions.SameSite" /> property should be |
|||
/// set to <see cref="Unspecified" />. |
|||
/// </summary> |
|||
/// <remarks> |
|||
/// This code is taken from Microsoft: |
|||
/// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ |
|||
/// </remarks> |
|||
/// <param name="userAgent">The user agent string to check.</param> |
|||
/// <returns>Whether the specified user agent (browser) accepts SameSite=None or not.</returns> |
|||
private static bool DisallowsSameSiteNone(string userAgent) |
|||
{ |
|||
// Cover all iOS based browsers here. This includes: |
|||
// - Safari on iOS 12 for iPhone, iPod Touch, iPad |
|||
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad |
|||
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad |
|||
// All of which are broken by SameSite=None, because they use the |
|||
// iOS networking stack. |
|||
// Notes from Thinktecture: |
|||
// Regarding https://caniuse.com/#search=samesite iOS versions lower |
|||
// than 12 are not supporting SameSite at all. Starting with version 13 |
|||
// unknown values are NOT treated as strict anymore. Therefore we only |
|||
// need to check version 12. |
|||
if (userAgent.Contains("CPU iPhone OS 12") |
|||
|| userAgent.Contains("iPad; CPU OS 12")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// Cover Mac OS X based browsers that use the Mac OS networking stack. |
|||
// This includes: |
|||
// - Safari on Mac OS X. |
|||
// This does not include: |
|||
// - Chrome on Mac OS X |
|||
// because they do not use the Mac OS networking stack. |
|||
// Notes from Thinktecture: |
|||
// Regarding https://caniuse.com/#search=samesite MacOS X versions lower |
|||
// than 10.14 are not supporting SameSite at all. Starting with version |
|||
// 10.15 unknown values are NOT treated as strict anymore. Therefore we |
|||
// only need to check version 10.14. |
|||
if (userAgent.Contains("Safari") |
|||
&& userAgent.Contains("Macintosh; Intel Mac OS X 10_14") |
|||
&& userAgent.Contains("Version/")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// Cover Chrome 50-69, because some versions are broken by SameSite=None |
|||
// and none in this range require it. |
|||
// Note: this covers some pre-Chromium Edge versions, |
|||
// but pre-Chromium Edge does not require SameSite=None. |
|||
// Notes from Thinktecture: |
|||
// We can not validate this assumption, but we trust Microsofts |
|||
// evaluation. And overall not sending a SameSite value equals to the same |
|||
// behavior as SameSite=None for these old versions anyways. |
|||
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (GetChromeVersion(userAgent) >= 80) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static int GetChromeVersion(string userAgent) |
|||
{ |
|||
try |
|||
{ |
|||
return Convert.ToInt32(userAgent.Split("Chrome/")[1].Split('.')[0]); |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Step-2 |
|||
|
|||
Assume that your project name is *Acme.BookStore*. Then open `AcmeBookStoreWebModule.cs` class. |
|||
|
|||
Add the following line to `ConfigureServices()` method. |
|||
|
|||
```csharp |
|||
context.Services.ConfigureNonBreakingSameSiteCookies(); |
|||
``` |
|||
### Step-3 |
|||
|
|||
Go to`OnApplicationInitialization()` method in `AcmeBookStoreWebModule.cs` add `app.UseCookiePolicy();` |
|||
|
|||
```csharp |
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
var env = context.GetEnvironment(); |
|||
|
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
else |
|||
{ |
|||
app.UseErrorPage(); |
|||
app.UseHsts(); |
|||
} |
|||
|
|||
app.UseCookiePolicy(); //<--- added this ---> |
|||
|
|||
//.... |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
It's all! You are ready to go! |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
Referenced from https://www.thinktecture.com/en/identity/samesite/prepare-your-identityserver/ |
|||
@ -0,0 +1,302 @@ |
|||
## Using DevExtreme with ABP Based Applications |
|||
|
|||
Hi, in this step by step article, I will show you how to integrate DevExtreme components into ABP Framework based applications. |
|||
|
|||
 |
|||
|
|||
## Create the Project |
|||
|
|||
ABP Framework offers startup templates to get into the business faster. We can download a new startup template using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): |
|||
|
|||
````bash |
|||
abp new DevExtremeSample |
|||
```` |
|||
|
|||
After the download is finished, open the solution in the Visual Studio (or your favorite IDE): |
|||
|
|||
 |
|||
|
|||
Run the `DevExtremeSample.DbMigrator` application to create the database and seed initial data (which creates the admin user, admin role, related permissions, etc). Then we can run the `DevExtremeSample.Web` project to see our application working. |
|||
|
|||
> _Default admin username is **admin** and password is **1q2w3E\***_ |
|||
|
|||
## Install DevExtreme |
|||
|
|||
You can follow [this documentation](https://js.devexpress.com/Documentation/17_1/Guide/ASP.NET_MVC_Controls/Prerequisites_and_Installation/) to install DevExpress packages into your computer. |
|||
|
|||
> Don't forget to add _"DevExpress NuGet Feed"_ to your **Nuget Package Sources**. |
|||
|
|||
### Adding DevExtreme NuGet Packages |
|||
|
|||
Add the `DevExtreme.AspNet.Core` NuGet package to the `DevExtremeSample.Application.Contracts` project. |
|||
|
|||
``` |
|||
Install-Package DevExtreme.AspNet.Core |
|||
``` |
|||
|
|||
Add the `DevExtreme.AspNet.Data` package to your `DevExtremeSample.Web` project. |
|||
|
|||
``` |
|||
Install-Package DevExtreme.AspNet.Data |
|||
``` |
|||
|
|||
### Adding DevExtreme NPM Dependencies |
|||
|
|||
Open your `DevExtremeSample.Web` project folder with a command line and add `devextreme` and `devextreme-aspnet-data` NPM packages: |
|||
|
|||
````bash |
|||
npm install devextreme |
|||
```` |
|||
|
|||
````bash |
|||
npm install devextreme-aspnet-data |
|||
```` |
|||
|
|||
### Adding Resource Mappings |
|||
|
|||
The `devextreme` and `devextreme-aspnet-data` NPM packages are saved under `node_modules` folder. We need to move the needed files in our `wwwroot/libs` folder to use them in our web project. We can do it using the ABP [client side resource mapping](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Client-Side-Package-Management) system. |
|||
|
|||
Open the `abp.resourcemapping.js` file in your `DevExtremeSample.Web` project and add the following definitions to inside `mappings` object. |
|||
|
|||
````json |
|||
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/", |
|||
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/js/" |
|||
```` |
|||
|
|||
The final `abp.resourcemapping.js` file should look like below: |
|||
|
|||
``` |
|||
module.exports = { |
|||
aliases: {}, |
|||
mappings: { |
|||
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/", |
|||
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/" |
|||
}, |
|||
}; |
|||
``` |
|||
|
|||
Open your `DevExtremeSample.Web` project folder with a command line and run the `gulp` command. This command will copy the needed library files into the ``/wwwroot/libs/devextreme/` folder. |
|||
|
|||
 |
|||
|
|||
You can see `devextreme` folder inside the `wwwroot/libs`: |
|||
|
|||
 |
|||
|
|||
### Adding DevExtremeStyleContributor |
|||
|
|||
We will add DevExtreme CSS files to the global bundle by creating a [bundle contributor](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification). |
|||
|
|||
Create a `Bundling` folder in the `DevExtremeSample.Web` project and a `DevExtremeStyleContributor.cs` file with the following content: |
|||
|
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace DevExtremeSample.Web.Bundling |
|||
{ |
|||
public class DevExtremeStyleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/devextreme/css/dx.common.css"); |
|||
context.Files.AddIfNotContains("/libs/devextreme/css/dx.light.css"); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> You can choose another theme than the light theme. Check the `/libs/devextreme/css/` folder and the DevExtreme documentation for other themes. |
|||
|
|||
Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code into the `ConfigureServices` method: |
|||
|
|||
```csharp |
|||
Configure<AbpBundlingOptions>(options => |
|||
{ |
|||
options |
|||
.StyleBundles |
|||
.Get(StandardBundles.Styles.Global) |
|||
.AddContributors(typeof(DevExtremeStyleContributor)); |
|||
}); |
|||
``` |
|||
|
|||
### Adding DevExtremeScriptContributor |
|||
|
|||
We can not add DevExtreme js packages to Global Script Bundles, just like done for the CSS files. Because DevExtreme requires to add its JavaScript files into the `<head>` section of the HTML document, while ABP Framework adds all JavaScript files to the end of the `<body>` (as a best practice). |
|||
|
|||
Fortunately, ABP Framework has a [layout hook system](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface#layout-hooks) that allows you to add any code into some specific positions in the HTML document. All you need to do is to create a `ViewComponent` and configure the layout hooks. |
|||
|
|||
Let's begin by creating a `DevExtremeScriptContributor.cs` file in the `Bundling` folder by copying the following code inside it: |
|||
|
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace DevExtremeSample.Web.Bundling |
|||
{ |
|||
[DependsOn( |
|||
typeof(JQueryScriptContributor) |
|||
)] |
|||
public class DevExtremeScriptContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/devextreme/js/dx.all.js"); |
|||
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.mvc.js"); |
|||
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.data.js"); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
As you see, the `DevExtremeScriptContributor` is depends on `JQueryScriptContributor` which adds JQuery related files before the DevExpress packages (see the [bundling system](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification) for details). |
|||
|
|||
#### Create DevExtremeJsViewComponent |
|||
|
|||
Create a new view component, named `DevExtremeJsViewComponent` inside the `/Components/DevExtremeJs` folder of the Web project, by following the steps below: |
|||
|
|||
1) Create a `DevExtremeJsViewComponent` class inside the `/Components/DevExtremeJs` (create the folders first): |
|||
|
|||
```csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace DevExtremeSample.Web.Components.DevExtremeJs |
|||
{ |
|||
public class DevExtremeJsViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View("/Components/DevExtremeJs/Default.cshtml"); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
2) Create `Default.cshtml` file in the same folder with the following content: |
|||
|
|||
```csharp |
|||
@using DevExtremeSample.Web.Bundling |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling |
|||
|
|||
<abp-script type="typeof(DevExtremeScriptContributor)" /> |
|||
``` |
|||
|
|||
Your final Web project should be like as the following: |
|||
|
|||
 |
|||
|
|||
3) Now, we can add this view component to `<head>` section by using the layout hooks. |
|||
|
|||
Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code into the `ConfigureServices` method: |
|||
|
|||
```csharp |
|||
Configure<AbpLayoutHookOptions>(options => |
|||
{ |
|||
options.Add( |
|||
LayoutHooks.Head.Last, //The hook name |
|||
typeof(DevExtremeJsViewComponent) //The component to add |
|||
); |
|||
}); |
|||
``` |
|||
|
|||
#### Known Issue: Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object. |
|||
|
|||
> This issue does exists in the ABP Framework v3.0 and earlier versions. If you are using ABP Framework v3.1 or a latter version, you can skip this section. |
|||
|
|||
When you run your `*.Web` project, you will see an exception (`Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.`) at your console. |
|||
|
|||
To fix that issue, download this file [abp.jquery.js](https://github.com/abpframework/abp/blob/dev/npm/packs/jquery/src/abp.jquery.js) and replace with the `wwwroot/libs/abp/jquery/abp.jquery.js` file of your Web project. |
|||
|
|||
### Result |
|||
|
|||
The installation step was done. You can use any DevExtreme component in your application. |
|||
|
|||
Example: A button and a progress bar component: |
|||
|
|||
 |
|||
|
|||
This example has been created by following [this documentation](https://js.devexpress.com/Demos/WidgetsGallery/Demo/ProgressBar/Overview/NetCore/Light/). |
|||
|
|||
## The Sample Application |
|||
|
|||
We have created a sample application with [Tree List](https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/) and [Data Grid](https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/) examples. |
|||
|
|||
### The Source Code |
|||
|
|||
You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Mvc). |
|||
|
|||
### Data Grid |
|||
|
|||
You can see the full working example of [Data Grid](https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/). |
|||
|
|||
 |
|||
|
|||
The related files for this example are highlighted at the following screenshots. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
### Tree List |
|||
|
|||
|
|||
You can see the full working example of [Tree List](https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/). |
|||
|
|||
 |
|||
|
|||
The related files for this example are highlighted at the following screenshots. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
### Additional Notes |
|||
|
|||
#### Data Storage |
|||
|
|||
I've used an in-memory list to store data for this example, instead of a real database. Because it is not related to DevExpress usage. There is a `SampleDataService.cs` file in `Data` folder at `.Application.Contracts` project. All the data is stored here. |
|||
|
|||
#### JSON Serialization |
|||
|
|||
You can see some `JsonProperty` attributes on the DTO properties. I uses these attributes because DevExtreme example expects `PascalCase` property names in the serialized JSON that is sent to the client. But ABP Framework & ASP.NET Core conventionally uses `camelCase` property names on JSON serialization. Adding these `JsonProperty` attributes ensures that the related properties are serialized as `PascalCase`. |
|||
|
|||
#### DevExtreme Components vs Application Service Methods |
|||
|
|||
ABP Framework conventionally converts application services to API Controllers. For example, see the application service below: |
|||
|
|||
````csharp |
|||
public class OrderAppService : DevExtremeSampleAppService, IOrderAppService |
|||
{ |
|||
public async Task<LoadResult> GetOrdersAsync(DataSourceLoadOptions loadOptions) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
public async Task<Order> InsertOrder(string values) |
|||
{ |
|||
... |
|||
} |
|||
... |
|||
} |
|||
```` |
|||
|
|||
You can use these service methods for your DevExtreme components as shown below: |
|||
|
|||
```csharp |
|||
Html.DevExtreme().DataGrid<Order>() |
|||
.DataSource(d => d.Mvc() |
|||
.Controller("Order") // Application Service Name without 'AppService' |
|||
.LoadAction("GetOrders") // Method Name without 'Async' |
|||
.InsertAction("InsertOrder") |
|||
.UpdateAction("UpdateOrder") |
|||
.DeleteAction("DeleteOrder") |
|||
.Key("OrderID") |
|||
) |
|||
``` |
|||
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
@ -1,3 +1,250 @@ |
|||
# Emailing |
|||
# Email Sending |
|||
|
|||
TODO! |
|||
ABP Framework provides various services, settings and integrations for sending emails; |
|||
|
|||
* Provides `IEmailSender` service that is used to send emails. |
|||
* Defines [settings](Settings.md) to configure email sending. |
|||
* Integrates to the [background job system](Background-Jobs.md) to send emails via background jobs. |
|||
* Provides [MailKit integration](MailKit.md) package. |
|||
|
|||
## Installation |
|||
|
|||
> This package is already installed if you are using the [application startup template](Startup-Templates/Application.md). |
|||
|
|||
It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.Emailing |
|||
```` |
|||
|
|||
If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Emailing). |
|||
|
|||
## Sending Emails |
|||
|
|||
### IEmailSender |
|||
|
|||
[Inject](Dependency-Injection.md) the `IEmailSender` into any service and use the `SendAsync` method to send emails. |
|||
|
|||
**Example** |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Emailing; |
|||
|
|||
namespace MyProject |
|||
{ |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IEmailSender _emailSender; |
|||
|
|||
public MyService(IEmailSender emailSender) |
|||
{ |
|||
_emailSender = emailSender; |
|||
} |
|||
|
|||
public async Task DoItAsync() |
|||
{ |
|||
await _emailSender.SendAsync( |
|||
"target@domain.com", // target email address |
|||
"Email subject", // subject |
|||
"This is email body..." // email body |
|||
); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`SendAsync` method has overloads to supply more parameters like; |
|||
|
|||
* **from**: You can set this as the first argument to set a sender email address. If not provided, the default sender address is used (see the email settings below). |
|||
* **isBodyHtml**: Indicates whether the email body may contain HTML tags. **Default: true**. |
|||
|
|||
> `IEmailSender` is the suggested way to send emails, since it makes your code provider independent. |
|||
|
|||
#### MailMessage |
|||
|
|||
In addition to primitive parameters, you can pass a **standard `MailMessage` object** ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.mailmessage)) to the `SendAsync` method to set more options, like adding attachments. |
|||
|
|||
### ISmtpEmailSender |
|||
|
|||
Sending emails is implemented by the standard `SmtpClient` class ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient)) by default. The implementation class is the `SmtpEmailSender`. This class also expose the `ISmtpEmailSender` service (in addition to the `IEmailSender`). |
|||
|
|||
Most of the time you want to directly use the `IEmailSender` to make your code provider independent. However, if you want to create an `SmtpClient` object with the same email settings, you can inject the `ISmtpEmailSender` and use its `BuildClientAsync` method to obtain a `SmtpClient` object and send the email yourself. |
|||
|
|||
## Queueing Emails / Background Jobs |
|||
|
|||
`IEmailSender` has a `QueueAsync` method that can be used to add emails to the background job queue to send them in a background thread. In this way, you don't take time of the user by waiting to send the email. `QueueAsync` method gets the same arguments with the `SendAsync` method. |
|||
|
|||
Queueing emails tolerates errors since the background job system has re-try mechanism to overcome temporary network/server problems. |
|||
|
|||
See the [background jobs document](Background-Jobs.md) for more about the background job system. |
|||
|
|||
## Email Settings |
|||
|
|||
Email sending uses the [setting system](Settings.md) to define settings and get the values of these settings on the runtime. `Volo.Abp.Emailing.EmailSettingNames` defines constants for the setting names, just listed below: |
|||
|
|||
* **Abp.Mailing.DefaultFromAddress**: Used as the sender's email address when you don't specify a sender when sending emails (just like in the example above). |
|||
* **Abp.Mailing.DefaultFromDisplayName**: Used as the sender's display name when you don't specify a sender when sending emails (just like in the example above). |
|||
* **Abp.Mailing.Smtp.Host**: The IP/Domain of the SMTP server (default: 127.0.0.1). |
|||
* **Abp.Mailing.Smtp.Port**: The Port of the SMTP server (default: 25). |
|||
* **Abp.Mailing.Smtp.UserName**: Username, if the SMTP server requires authentication. |
|||
* **Abp.Mailing.Smtp.Password**: Password, if the SMTP server requires authentication. **This value is encrypted **(see the section below). |
|||
* **Abp.Mailing.Smtp.Domain**: Domain for the username, if the SMTP server requires authentication. |
|||
* **Abp.Mailing.Smtp.EnableSsl**: A value that indicates if the SMTP server uses SSL or not ("true" or "false". Default: "false"). |
|||
* **Abp.Mailing.Smtp.UseDefaultCredentials**: If true, uses default credentials instead of the provided username and password ("true" or "false". Default: "true"). |
|||
|
|||
The easiest way to define these settings it to add them to the `appsettings.json` file. The [application startup template](Startup-Templates/Application.md) already has these settings in the `appsettings.json`: |
|||
|
|||
````json |
|||
"Settings": { |
|||
"Abp.Mailing.Smtp.Host": "127.0.0.1", |
|||
"Abp.Mailing.Smtp.Port": "25", |
|||
"Abp.Mailing.Smtp.UserName": "", |
|||
"Abp.Mailing.Smtp.Password": "", |
|||
"Abp.Mailing.Smtp.Domain": "", |
|||
"Abp.Mailing.Smtp.EnableSsl": "false", |
|||
"Abp.Mailing.Smtp.UseDefaultCredentials": "true", |
|||
"Abp.Mailing.DefaultFromAddress": "noreply@abp.io", |
|||
"Abp.Mailing.DefaultFromDisplayName": "ABP application" |
|||
} |
|||
```` |
|||
|
|||
You can set/change these settings using the `ISettingManager` and store values in a database. See the [setting system document](Settings.md) to understand the setting system better. |
|||
|
|||
### Encrypt the SMTP Password |
|||
|
|||
*Abp.Mailing.Smtp.Password* must be an **encrypted** value. If you use the `ISettingManager` to set the password, you don't have to worry. It internally encrypts the values on set and decrypts on get. |
|||
|
|||
If you use the `appsettings.json` to store the password, you should manually inject the `ISettingEncryptionService` and use its `Encrypt` method to obtain an encrypted value. This can be done by creating a simple code in your application. Then you can delete the code. As better, you can create a UI in your application to configure the email settings. In this case, you can directly use the `ISettingManager` without worrying the encryption. |
|||
|
|||
### ISmtpEmailSenderConfiguration |
|||
|
|||
If you don't want to use the setting system to store the email sending configuration, you can replace the `ISmtpEmailSenderConfiguration` service with your own implementation to get the configuration from any other source. `ISmtpEmailSenderConfiguration` is implemented by the `SmtpEmailSenderConfiguration` by default, which gets the configuration from the setting system as explained above. |
|||
|
|||
## Text Template Integration |
|||
|
|||
ABP Framework provides a strong and flexible [text templating system](Text-Templating.md). You can use the text templating system to create dynamic email contents. Inject the `ITemplateRenderer` and use the `RenderAsync` to render a template. Then use the result as the email body. |
|||
|
|||
While you can define and use your own text templates, email sending system provides two simple built-in text templates. |
|||
|
|||
**Example: Use the standard and simple message template to send emails** |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.Emailing.Templates; |
|||
using Volo.Abp.TextTemplating; |
|||
|
|||
namespace Acme.BookStore.Web |
|||
{ |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IEmailSender _emailSender; |
|||
private readonly ITemplateRenderer _templateRenderer; |
|||
|
|||
public MyService( |
|||
IEmailSender emailSender, |
|||
ITemplateRenderer templateRenderer) |
|||
{ |
|||
_emailSender = emailSender; |
|||
_templateRenderer = templateRenderer; |
|||
} |
|||
|
|||
public async Task DoItAsync() |
|||
{ |
|||
var body = await _templateRenderer.RenderAsync( |
|||
StandardEmailTemplates.Message, |
|||
new |
|||
{ |
|||
message = "This is email body..." |
|||
} |
|||
); |
|||
|
|||
await _emailSender.SendAsync( |
|||
"target-address@domain.com", |
|||
"Email subject", |
|||
body |
|||
); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
The resulting email body will be shown below: |
|||
|
|||
````html |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
This is email body... |
|||
</body> |
|||
</html> |
|||
```` |
|||
|
|||
Emailing system defines the built-in text templates with the given names: |
|||
|
|||
"**Abp.StandardEmailTemplates.Message**" is simplest template that has a text message: |
|||
|
|||
````html |
|||
{%{{{model.message}}}%} |
|||
```` |
|||
|
|||
This template uses the "Abp.StandardEmailTemplates.Layout" as its layout. |
|||
|
|||
"**Abp.StandardEmailTemplates.Layout**" is a simple template to provide an HTML document layout: |
|||
|
|||
````html |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
{%{{{content}}}%} |
|||
</body> |
|||
</html> |
|||
```` |
|||
|
|||
The final rendered message was shown above. |
|||
|
|||
> These template names are contants defined in the `Volo.Abp.Emailing.Templates.StandardEmailTemplates` class. |
|||
|
|||
### Overriding/Replacing the Standard Templates |
|||
|
|||
You typically want to replace the standard templates with your own ones, so you can prepare a branded email messages. To do that, you can use the power of the [virtual file system](Virtual-File-System.md) (VFS) or replace them in your own template definition provider. |
|||
|
|||
Pathes of the templates in the virtual file system are shown below: |
|||
|
|||
* `/Volo/Abp/Emailing/Templates/Layout.tpl` |
|||
* `/Volo/Abp/Emailing/Templates/Message.tpl` |
|||
|
|||
If you add files to the same localization in the virtual file system, your files will override them. |
|||
|
|||
Templates are inline localized, that means you can take the power of the [localization system](Localization.md) to make your templates multi-cultural. |
|||
|
|||
See the [text templating system](Text-Templating.md) document for details. |
|||
|
|||
> Notice that you can define and use your own templates for your application, rather than using the standard simple templates. These standard templates are mostly for reusable modules where they don't define their own templates but rely on the built-in ones. This makes easy to customize emails sent by the used modules, by just overriding the standard email layout template. |
|||
|
|||
## NullEmailSender |
|||
|
|||
`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](Logging.md), rathen than actually sending the emails. |
|||
|
|||
This class can be useful especially in development time where you generally don't want to send real emails. The [application startup template](Startup-Templates/Application.md) already uses this class in the **DEBUG mode** with the following configuration in the domain layer: |
|||
|
|||
````csharp |
|||
#if DEBUG |
|||
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); |
|||
#endif |
|||
```` |
|||
|
|||
So, don't confuse if you don't receive emails on DEBUG mode. Emails will be sent as expected on production (RELEASE mode). Remove these lines if you want to send real emails on DEBUG too. |
|||
|
|||
## See Also |
|||
|
|||
* [MailKit integration for sending emails](MailKit.md) |
|||
@ -0,0 +1,3 @@ |
|||
# Global Features |
|||
|
|||
TODO (see [#5061](https://github.com/abpframework/abp/issues/5061) until this is documented). |
|||
@ -0,0 +1,48 @@ |
|||
# MailKit Integration |
|||
|
|||
[MailKit](http://www.mimekit.net/) is a cross-platform, popular open source mail client library for .net. ABP Framework provides an integration package to use the MailKit as the [email sender](Emailing.md). |
|||
|
|||
## Installation |
|||
|
|||
It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.MailKit |
|||
```` |
|||
|
|||
If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.MailKit). |
|||
|
|||
## Sending Emails |
|||
|
|||
### IEmailSender |
|||
|
|||
[Inject](Dependency-Injection.md) the standard `IEmailSender` into any service and use the `SendAsync` method to send emails. See the [email sending document](Emailing.md) for details. |
|||
|
|||
> `IEmailSender` is the suggested way to send emails even if you use MailKit, since it makes your code provider independent. |
|||
|
|||
### IMailKitSmtpEmailSender |
|||
|
|||
MailKit package also exposes the `IMailKitSmtpEmailSender` service that extends the `IEmailSender` by adding the `BuildClientAsync()` method. This method can be used to obtain a `MailKit.Net.Smtp.SmtpClient` object that can be used to perform MailKit specific operations. |
|||
|
|||
## Configuration |
|||
|
|||
MailKit integration package uses the same settings defined by the email sending system. So, refer to the [email sending document](Emailing.md) for the settings. |
|||
|
|||
In addition to the standard settings, this package defines `AbpMailKitOptions` as a simple [options](Options.md) class. This class defines only one options: |
|||
|
|||
* **SecureSocketOption**: Used to set one of the `SecureSocketOptions`. Default: `null` (uses the defaults). |
|||
|
|||
**Example: Use *SecureSocketOptions.SslOnConnect*** |
|||
|
|||
````csharp |
|||
Configure<AbpMailKitOptions>(options => |
|||
{ |
|||
options.SecureSocketOption = SecureSocketOptions.SslOnConnect; |
|||
}); |
|||
```` |
|||
|
|||
Refer to the [MailKit documentation](http://www.mimekit.net/) to learn more about this option. |
|||
|
|||
## See Also |
|||
|
|||
* [Email sending](Emailing.md) |
|||
@ -0,0 +1,3 @@ |
|||
# Module Entity Extensions |
|||
|
|||
See https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions (it will be moved here soon). |
|||
@ -0,0 +1,40 @@ |
|||
# Preview Releases |
|||
|
|||
The preview versions are released **~2 weeks before** releasing a major or feature version of the ABP Framework. They are released for developers to try and provide feedback to have more stable versions. |
|||
|
|||
Versioning of a preview release is like that: |
|||
|
|||
* 3.1.0-rc.1 |
|||
* 4.0.0-rc.1 |
|||
|
|||
More than one preview releases (like 3.1.0-rc.2 and 3.1.0-rc.3) might be published until the stable version (like 3.1.0). |
|||
|
|||
## Using the Preview Versions |
|||
|
|||
### New Solutions |
|||
|
|||
To create a project for testing the preview version, you can select the "**preview**" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the [ABP CLI](CLI.md) new command: |
|||
|
|||
````bash |
|||
abp new Acme.BookStore --preview |
|||
```` |
|||
|
|||
This command will create a new project using the latest preview NuGet packages, NPM packages and the solution template. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution. |
|||
|
|||
### Existing Solutions |
|||
|
|||
If you already have a solution and want to use/test the latest preview version, use the following [ABP CLI](CLI.md) command in the root folder of your solution. |
|||
|
|||
````bash |
|||
abp switch-to-preview |
|||
```` |
|||
|
|||
You can return back to the latest stable using the `abp switch-to-stable ` command later. |
|||
|
|||
````bash |
|||
abp switch-to-stable |
|||
```` |
|||
|
|||
## Providing Feedback |
|||
|
|||
You can open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new), if you find a bug or want to provide any kind of feedback. |
|||
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.UI |
|||
{ |
|||
public class AbpMvcUiOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Default value: "/Account/Login".
|
|||
/// </summary>
|
|||
public string LoginUrl { get; set; } = "/Account/Login"; |
|||
|
|||
/// <summary>
|
|||
/// Default value: "/Account/Logout".
|
|||
/// </summary>
|
|||
public string LogoutUrl { get; set; } = "/Account/Logout"; |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.GlobalFeatures; |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures |
|||
{ |
|||
public class GlobalFeatureActionFilter : IAsyncActionFilter, ITransientDependency |
|||
{ |
|||
public ILogger<GlobalFeatureActionFilter> Logger { get; set; } |
|||
|
|||
public GlobalFeatureActionFilter() |
|||
{ |
|||
Logger = NullLogger<GlobalFeatureActionFilter>.Instance; |
|||
} |
|||
|
|||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) |
|||
{ |
|||
if (!context.ActionDescriptor.IsControllerAction()) |
|||
{ |
|||
await next(); |
|||
return; |
|||
} |
|||
|
|||
if (!IsGlobalFeatureEnabled(context.Controller.GetType(), out var attribute)) |
|||
{ |
|||
Logger.LogWarning($"The '{context.Controller.GetType().FullName}' controller needs to enable '{attribute.Name}' feature."); |
|||
context.Result = new NotFoundResult(); |
|||
return; |
|||
} |
|||
|
|||
await next(); |
|||
} |
|||
|
|||
protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute) |
|||
{ |
|||
attribute = ReflectionHelper.GetSingleAttributeOrDefault<RequiresGlobalFeatureAttribute>(controllerType); |
|||
return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.GlobalFeatures; |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures |
|||
{ |
|||
public class GlobalFeaturePageFilter: IAsyncPageFilter, ITransientDependency |
|||
{ |
|||
public ILogger<GlobalFeaturePageFilter> Logger { get; set; } |
|||
|
|||
public GlobalFeaturePageFilter() |
|||
{ |
|||
Logger = NullLogger<GlobalFeaturePageFilter>.Instance; |
|||
} |
|||
|
|||
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) |
|||
{ |
|||
if (context.HandlerInstance == null || !context.ActionDescriptor.IsPageAction()) |
|||
{ |
|||
await next(); |
|||
return; |
|||
} |
|||
|
|||
if (!IsGlobalFeatureEnabled(context.HandlerInstance.GetType(), out var attribute)) |
|||
{ |
|||
Logger.LogWarning($"The '{context.HandlerInstance.GetType().FullName}' page needs to enable '{attribute.Name}' feature."); |
|||
context.Result = new NotFoundResult(); |
|||
return; |
|||
} |
|||
|
|||
await next(); |
|||
} |
|||
|
|||
protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute) |
|||
{ |
|||
attribute = ReflectionHelper.GetSingleAttributeOrDefault<RequiresGlobalFeatureAttribute>(controllerType); |
|||
return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.AspNetCore.ExceptionHandling |
|||
{ |
|||
public class AbpExceptionHandlingOptions |
|||
{ |
|||
public bool SendExceptionsDetailsToClients { get; set; } = false; |
|||
} |
|||
} |
|||
@ -0,0 +1,269 @@ |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq.Expressions; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace System.Linq |
|||
{ |
|||
// Codes below are taken from https://github.com/scottksmith95/LINQKit project.
|
|||
|
|||
/// <summary> The Predicate Operator </summary>
|
|||
public enum PredicateOperator |
|||
{ |
|||
/// <summary> The "Or" </summary>
|
|||
Or, |
|||
|
|||
/// <summary> The "And" </summary>
|
|||
And |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// See http://www.albahari.com/expressions for information and examples.
|
|||
/// </summary>
|
|||
public static class PredicateBuilder |
|||
{ |
|||
private class RebindParameterVisitor : ExpressionVisitor |
|||
{ |
|||
private readonly ParameterExpression _oldParameter; |
|||
private readonly ParameterExpression _newParameter; |
|||
|
|||
public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) |
|||
{ |
|||
_oldParameter = oldParameter; |
|||
_newParameter = newParameter; |
|||
} |
|||
|
|||
protected override Expression VisitParameter(ParameterExpression node) |
|||
{ |
|||
return node == _oldParameter ? _newParameter : base.VisitParameter(node); |
|||
} |
|||
} |
|||
|
|||
/// <summary> Start an expression </summary>
|
|||
public static ExpressionStarter<T> New<T>(Expression<Func<T, bool>> expr = null) |
|||
{ |
|||
return new ExpressionStarter<T>(expr); |
|||
} |
|||
|
|||
/// <summary> Create an expression with a stub expression true or false to use when the expression is not yet started. </summary>
|
|||
public static ExpressionStarter<T> New<T>(bool defaultExpression) |
|||
{ |
|||
return new ExpressionStarter<T>(defaultExpression); |
|||
} |
|||
|
|||
/// <summary> OR </summary>
|
|||
public static Expression<Func<T, bool>> Or<T>([NotNull] this Expression<Func<T, bool>> expr1, |
|||
[NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); |
|||
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters); |
|||
} |
|||
|
|||
/// <summary> AND </summary>
|
|||
public static Expression<Func<T, bool>> And<T>([NotNull] this Expression<Func<T, bool>> expr1, |
|||
[NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); |
|||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
/// <param name="first">The source Predicate.</param>
|
|||
/// <param name="second">The second Predicate.</param>
|
|||
/// <param name="operator">The Operator (can be "And" or "Or").</param>
|
|||
/// <returns>Expression{Func{T, bool}}</returns>
|
|||
public static Expression<Func<T, bool>> Extend<T>([NotNull] this Expression<Func<T, bool>> first, |
|||
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or) |
|||
{ |
|||
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
/// <param name="first">The source Predicate.</param>
|
|||
/// <param name="second">The second Predicate.</param>
|
|||
/// <param name="operator">The Operator (can be "And" or "Or").</param>
|
|||
/// <returns>Expression{Func{T, bool}}</returns>
|
|||
public static Expression<Func<T, bool>> Extend<T>([NotNull] this ExpressionStarter<T> first, |
|||
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or) |
|||
{ |
|||
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
public class ExpressionStarter<T> |
|||
{ |
|||
public ExpressionStarter() : this(false) |
|||
{ |
|||
} |
|||
|
|||
public ExpressionStarter(bool defaultExpression) |
|||
{ |
|||
if (defaultExpression) |
|||
{ |
|||
DefaultExpression = f => true; |
|||
} |
|||
else |
|||
{ |
|||
DefaultExpression = f => false; |
|||
} |
|||
} |
|||
|
|||
public ExpressionStarter(Expression<Func<T, bool>> exp) : this(false) |
|||
{ |
|||
_predicate = exp; |
|||
} |
|||
|
|||
/// <summary>The actual Predicate. It can only be set by calling Start.</summary>
|
|||
private Expression<Func<T, bool>> Predicate => |
|||
(IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression; |
|||
|
|||
private Expression<Func<T, bool>> _predicate; |
|||
|
|||
/// <summary>Determines if the predicate is started.</summary>
|
|||
public bool IsStarted => _predicate != null; |
|||
|
|||
/// <summary> A default expression to use only when the expression is null </summary>
|
|||
public bool UseDefaultExpression => DefaultExpression != null; |
|||
|
|||
/// <summary>The default expression</summary>
|
|||
public Expression<Func<T, bool>> DefaultExpression { get; set; } |
|||
|
|||
/// <summary>Set the Expression predicate</summary>
|
|||
/// <param name="exp">The first expression</param>
|
|||
public Expression<Func<T, bool>> Start(Expression<Func<T, bool>> exp) |
|||
{ |
|||
if (IsStarted) |
|||
{ |
|||
throw new Exception("Predicate cannot be started again."); |
|||
} |
|||
|
|||
return _predicate = exp; |
|||
} |
|||
|
|||
/// <summary>Or</summary>
|
|||
public Expression<Func<T, bool>> Or([NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2); |
|||
} |
|||
|
|||
/// <summary>And</summary>
|
|||
public Expression<Func<T, bool>> And([NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2); |
|||
} |
|||
|
|||
/// <summary> Show predicate string </summary>
|
|||
public override string ToString() |
|||
{ |
|||
return Predicate?.ToString(); |
|||
} |
|||
|
|||
#region Implicit Operators
|
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator Expression<Func<T, bool>>(ExpressionStarter<T> right) |
|||
{ |
|||
return right?.Predicate; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator Func<T, bool>(ExpressionStarter<T> right) |
|||
{ |
|||
return right == null ? null : |
|||
(right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator ExpressionStarter<T>(Expression<Func<T, bool>> right) |
|||
{ |
|||
return right == null ? null : new ExpressionStarter<T>(right); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Implement Expression<TDelagate> methods and properties
|
|||
|
|||
#if !(NET35)
|
|||
|
|||
/// <summary></summary>
|
|||
public Func<T, bool> Compile() |
|||
{ |
|||
return Predicate.Compile(); |
|||
} |
|||
#endif
|
|||
|
|||
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
|
|||
/// <summary></summary>
|
|||
public Func<T, bool> Compile(DebugInfoGenerator debugInfoGenerator) { return Predicate.Compile(debugInfoGenerator); } |
|||
|
|||
/// <summary></summary>
|
|||
public Expression<Func<T, bool>> Update(Expression body, IEnumerable<ParameterExpression> parameters) { return Predicate.Update(body, parameters); } |
|||
#endif
|
|||
|
|||
#endregion
|
|||
|
|||
#region Implement LamdaExpression methods and properties
|
|||
|
|||
/// <summary></summary>
|
|||
public Expression Body => Predicate.Body; |
|||
|
|||
|
|||
/// <summary></summary>
|
|||
public ExpressionType NodeType => Predicate.NodeType; |
|||
|
|||
/// <summary></summary>
|
|||
public ReadOnlyCollection<ParameterExpression> Parameters => Predicate.Parameters; |
|||
|
|||
/// <summary></summary>
|
|||
public Type Type => Predicate.Type; |
|||
|
|||
#if !(NET35)
|
|||
/// <summary></summary>
|
|||
public string Name => Predicate.Name; |
|||
|
|||
/// <summary></summary>
|
|||
public Type ReturnType => Predicate.ReturnType; |
|||
|
|||
/// <summary></summary>
|
|||
public bool TailCall => Predicate.TailCall; |
|||
#endif
|
|||
|
|||
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
|
|||
/// <summary></summary>
|
|||
public void CompileToMethod(MethodBuilder method) { Predicate.CompileToMethod(method); } |
|||
|
|||
/// <summary></summary>
|
|||
public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) { Predicate.CompileToMethod(method, debugInfoGenerator); } |
|||
|
|||
#endif
|
|||
|
|||
#endregion
|
|||
|
|||
#region Implement Expression methods and properties
|
|||
|
|||
#if !(NET35)
|
|||
/// <summary></summary>
|
|||
public virtual bool CanReduce => Predicate.CanReduce; |
|||
#endif
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,3 @@ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
[assembly:InternalsVisibleTo("Volo.Abp.GlobalFeatures.Tests")] |
|||